I have the following (simple) scenario in a community project: There is a simple messaging system where users can send messages to one or many other users (comparable to email). So a message has one user as sender and many users as recipients. I''ve modeled it like this: CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT, login VARCHAR(80) NOT NULL, ... primary key (id), INDEX (id) ) engine = InnoDB; create table messages ( id INT NOT NULL AUTO_INCREMENT, user_id int not null, subject varchar(255), body text, primary key(id) ) engine = InnoDB, character set utf8; create table message_users ( id INT NOT NULL AUTO_INCREMENT, message_id int not null, user_id int not null, primary key(id) ) engine = InnoDB, character set utf8; and in Rails: class Message < ActiveRecord::Base belongs_to :user # sender has_many :message_users has_many :users, :through => :message_user #recipients end class MessageRecipient < ActiveRecord::Base belongs_to :message belongs_to :user end class User < ActiveRecord::Base has_many :message_users has_many :messages, :through => :message_users end What I don''t like here is that a message has message.user as sender and message.users as recipients. I would like to be able to call something like this: message.sender message.recipients user.messages message.recipients << u Another problem might be that user.messages is not clearly defined in my code as it could be all messages the user has sent or all messages that a user has received. How can I achieve a better solution? Maybe you can help me out... -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I think this is a strong case for habtm, rather than has_many :through. I''m normally a big fan of :through and use it often, but I''ve come to realize that sometimes, habtm really is much more appropriate. First, I''d rename your message_users table to messages_recipients (note: both message and recipient are plural), and change the user_id column in that table to recipient_id. Then.... (disclaimer: this code is untested, so there may be some typos and other goofs) class Message < ActiveRecord::Base belongs_to :sender, :class_name => ''User'' has_and_belongs_to_many :recipients, :class_name => ''User'' end class User < ActiveRecord::Base has_many :sent_messages, :class_name => ''Message'' has_and_belongs_to_many :received_messages, :class_name => ''Message'' end now... @user.sent_messages @user.received_messages @message.sender @message.recipients voila! get rid of your MessageRecipient class. You don''t need it. Star Burger wrote:> I have the following (simple) scenario in a community project: > > There is a simple messaging system where users can send messages to one > or many other users (comparable to email). So a message has one user as > sender and many users as recipients. I''ve modeled it like this: > > CREATE TABLE users ( > id INT NOT NULL AUTO_INCREMENT, > login VARCHAR(80) NOT NULL, > ... > primary key (id), > INDEX (id) > ) engine = InnoDB; > > create table messages ( > id INT NOT NULL AUTO_INCREMENT, > user_id int not null, > subject varchar(255), > body text, > primary key(id) > ) engine = InnoDB, character set utf8; > > create table message_users ( > id INT NOT NULL AUTO_INCREMENT, > message_id int not null, > user_id int not null, > primary key(id) > ) engine = InnoDB, character set utf8; > > and in Rails: > > class Message < ActiveRecord::Base > belongs_to :user # sender > has_many :message_users > has_many :users, :through => :message_user #recipients > end > > class MessageRecipient < ActiveRecord::Base > belongs_to :message > belongs_to :user > end > > class User < ActiveRecord::Base > has_many :message_users > has_many :messages, :through => :message_users > end > > > What I don''t like here is that a message has message.user as sender and > message.users as recipients. I would like to be able to call something > like this: > > message.sender > message.recipients > user.messages > message.recipients << u > > Another problem might be that user.messages is not clearly defined in my > code as it could be all messages the user has sent or all messages that > a user has received. > > How can I achieve a better solution? Maybe you can help me out... > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Whoops, I knew I''d forget something... change this line in the User class to add the foreign_key. has_and_belongs_to_many :received_messages, :class_name => ''Message'', :foreign_key => :recipient_id --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I''m wondering why the has_many :through? Your join model has no extra attributes at all. Why not just Message belongs_to :sender has_and_belongs_to_many :recipients User has_many :sent_messages, :class_name => ''Message'' has_and_belongs_to_many :received_messages, :class_name => ''Message'' This gives you all the scenarios you desired and appears to be much simpler than the path you are currently going down. I''d love to hear other options using has_many :through with more attributes on the join model. -Michael http://javathehutt.blogspot.com On Mar 6, 2007, at 8:18 AM, Star Burger wrote:> > I have the following (simple) scenario in a community project: > > There is a simple messaging system where users can send messages to > one > or many other users (comparable to email). So a message has one > user as > sender and many users as recipients. I''ve modeled it like this: > > CREATE TABLE users ( > id INT NOT NULL AUTO_INCREMENT, > login VARCHAR(80) NOT NULL, > ... > primary key (id), > INDEX (id) > ) engine = InnoDB; > > create table messages ( > id INT NOT NULL AUTO_INCREMENT, > user_id int not null, > subject varchar(255), > body text, > primary key(id) > ) engine = InnoDB, character set utf8; > > create table message_users ( > id INT NOT NULL AUTO_INCREMENT, > message_id int not null, > user_id int not null, > primary key(id) > ) engine = InnoDB, character set utf8; > > and in Rails: > > class Message < ActiveRecord::Base > belongs_to :user # sender > has_many :message_users > has_many :users, :through => :message_user #recipients > end > > class MessageRecipient < ActiveRecord::Base > belongs_to :message > belongs_to :user > end > > class User < ActiveRecord::Base > has_many :message_users > has_many :messages, :through => :message_users > end > > > What I don''t like here is that a message has message.user as sender > and > message.users as recipients. I would like to be able to call something > like this: > > message.sender > message.recipients > user.messages > message.recipients << u > > Another problem might be that user.messages is not clearly defined > in my > code as it could be all messages the user has sent or all messages > that > a user has received. > > How can I achieve a better solution? Maybe you can help me out... > > -- > Posted via http://www.ruby-forum.com/. > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Jon Garvin wrote:> Whoops, I knew I''d forget something... change this line in the User > class to add the foreign_key. > > has_and_belongs_to_many :received_messages, :class_name => ''Message'', > :foreign_key => :recipient_idHmmm. That didn''t work for me. user.received_messages caused a look for table messages_users which is not there. Star -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
has_and_belongs_to_many automatically assumes that the join table for the association is named like this. tablename1_tablename2 where tablename1 is the table that comes first alphabetically. So for a has_anf_belong_to_many association, you have to rename your message_users table to messages_users, OR: has_and_belongs_to_many received_messages, :class_name => ''Message'', :foreign_key => :recipient_id, :join_table => "message_users" On 7 Mrz., 13:16, Star Burger <rails-mailing-l...-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> Jon Garvin wrote: > > Whoops, I knew I''d forget something... change this line in the User > > class to add the foreign_key. > > > has_and_belongs_to_many :received_messages, :class_name => ''Message'', > > :foreign_key => :recipient_id > > Hmmm. That didn''t work for me. user.received_messages caused a look for > table messages_users which is not there. > > Star > > -- > Posted viahttp://www.ruby-forum.com/.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Star Burger wrote:> Jon Garvin wrote: >> Whoops, I knew I''d forget something... change this line in the User >> class to add the foreign_key. >> >> has_and_belongs_to_many :received_messages, :class_name => ''Message'', >> :foreign_key => :recipient_id > > Hmmm. That didn''t work for me. user.received_messages caused a look for > table messages_users which is not there. > > StarThe following ist working via habtm: create table messages_users ( message_id int not null, user_id int not null, primary key(message_id, user_id) ) engine = InnoDB, character set utf8; class Message < ActiveRecord::Base belongs_to :sender, :class_name => ''User'' has_and_belongs_to_many :recipients, :class_name => ''User'', :foreign_key => ''message_id'' end class User < ActiveRecord::Base has_many :sent_messages, :class_name => ''Message'' has_and_belongs_to_many :received_messages, :class_name => ''Message'', :foreign_key => :user_id end Puuuh. Thanks for your advice. -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Hi Star, you should be able to do things that you need to do. The :through allows you to have alot more flexibility in my opinion in the event that you need to add additional field(s) to the join model. Thus, you should be able to do the following with your current models: message.user message.message_users message.users message.users << user1 user.message_users user.messages user.messages << message1 Please revisit the AWDwRv2 for complete list of dynamically generated methods using :through. Good luck, -Conrad On 3/6/07, Star Burger <rails-mailing-list-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote:> > I have the following (simple) scenario in a community project: > > There is a simple messaging system where users can send messages to one > or many other users (comparable to email). So a message has one user as > sender and many users as recipients. I''ve modeled it like this: > > CREATE TABLE users ( > id INT NOT NULL AUTO_INCREMENT, > login VARCHAR(80) NOT NULL, > ... > primary key (id), > INDEX (id) > ) engine = InnoDB; > > create table messages ( > id INT NOT NULL AUTO_INCREMENT, > user_id int not null, > subject varchar(255), > body text, > primary key(id) > ) engine = InnoDB, character set utf8; > > create table message_users ( > id INT NOT NULL AUTO_INCREMENT, > message_id int not null, > user_id int not null, > primary key(id) > ) engine = InnoDB, character set utf8; > > and in Rails: > > class Message < ActiveRecord::Base > belongs_to :user # sender > has_many :message_users > has_many :users, :through => :message_user #recipients > end > > class MessageRecipient < ActiveRecord::Base > belongs_to :message > belongs_to :user > end > > class User < ActiveRecord::Base > has_many :message_users > has_many :messages, :through => :message_users > end > > > What I don''t like here is that a message has message.user as sender and > message.users as recipients. I would like to be able to call something > like this: > > message.sender > message.recipients > user.messages > message.recipients << u > > Another problem might be that user.messages is not clearly defined in my > code as it could be all messages the user has sent or all messages that > a user has received. > > How can I achieve a better solution? Maybe you can help me out... > > -- > Posted via http://www.ruby-forum.com/. > > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Hey Conrad, list... So while it''s true that using has_many :through does give you that flexibility the fact that you now need to find a model for that join table adds extra complexity to your domain model that you wouldn''t otherwise have with a standard habtm relationship. But your point is well taken. If you think it''s quite possible for your join table to have additional attributes in the future making it a has_many :through from the start saves you the pain of a data migration later. But doing so as a manner of standard practice seems to be overkill IMHO. Best, -Michael http://javathehutt.blogspot.com On Mar 7, 2007, at 8:08 AM, Conrad Taylor wrote:> > Hi Star, you should be able to do things that you need to do. The > :through allows you to have alot more flexibility in my opinion in the > event that you need to add additional field(s) to the join model. > Thus, you should be able to do the following with your current models: > > message.user > message.message_users > message.users > message.users << user1 > user.message_users > user.messages > user.messages << message1 > > Please revisit the AWDwRv2 for complete list of dynamically generated > methods using :through. > > Good luck, > > -Conrad > > On 3/6/07, Star Burger <rails-mailing-list-ARtvInVfO7ksV2N9l4h3zg@public.gmane.org> wrote: >> >> I have the following (simple) scenario in a community project: >> >> There is a simple messaging system where users can send messages >> to one >> or many other users (comparable to email). So a message has one >> user as >> sender and many users as recipients. I''ve modeled it like this: >> >> CREATE TABLE users ( >> id INT NOT NULL AUTO_INCREMENT, >> login VARCHAR(80) NOT NULL, >> ... >> primary key (id), >> INDEX (id) >> ) engine = InnoDB; >> >> create table messages ( >> id INT NOT NULL AUTO_INCREMENT, >> user_id int not null, >> subject varchar(255), >> body text, >> primary key(id) >> ) engine = InnoDB, character set utf8; >> >> create table message_users ( >> id INT NOT NULL AUTO_INCREMENT, >> message_id int not null, >> user_id int not null, >> primary key(id) >> ) engine = InnoDB, character set utf8; >> >> and in Rails: >> >> class Message < ActiveRecord::Base >> belongs_to :user # sender >> has_many :message_users >> has_many :users, :through => :message_user #recipients >> end >> >> class MessageRecipient < ActiveRecord::Base >> belongs_to :message >> belongs_to :user >> end >> >> class User < ActiveRecord::Base >> has_many :message_users >> has_many :messages, :through => :message_users >> end >> >> >> What I don''t like here is that a message has message.user as >> sender and >> message.users as recipients. I would like to be able to call >> something >> like this: >> >> message.sender >> message.recipients >> user.messages >> message.recipients << u >> >> Another problem might be that user.messages is not clearly defined >> in my >> code as it could be all messages the user has sent or all messages >> that >> a user has received. >> >> How can I achieve a better solution? Maybe you can help me out... >> >> -- >> Posted via http://www.ruby-forum.com/. >> >>> >> > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I''ve implemented class Message < ActiveRecord::Base belongs_to :sender, :class_name => ''User'' has_and_belongs_to_many :recipients, :class_name => ''User'' end class User < ActiveRecord::Base has_many :sent_messages, :class_name => ''Message'' has_and_belongs_to_many :received_messages, :class_name => ''Message'' end with CREATE TABLE users ( id INT NOT NULL AUTO_INCREMENT, login VARCHAR(80) NOT NULL, ... primary key (id), INDEX (id) ) engine = InnoDB; create table messages ( id INT NOT NULL AUTO_INCREMENT, user_id int not null, subject varchar(255), body text, created_at DATETIME default NULL, modified_at DATETIME default NULL, primary key(id) ) engine = InnoDB, character set utf8; create table messages_users ( message_id int not null, user_id int not null, primary key(message_id, user_id) ) engine = InnoDB, character set utf8; First everything seemed fine. But user.received_messages delivers message objects that are DEFINITELY NOT in the database (wrong sender ID)!! I had only one singel message in the DB, but user.received_messages.first yields another object than Message.find :first Strange, strange!!! A bug maybe? -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Your tables and classes look correct to me. Assuming that you are creating your messages properly by adding the correct sender and recipients your data should be returned correctly when you access those relations. Write a unit test and see what is in the DB after you send a single message so you can be sure things are behaving as you suspect. -Michael http://javathehutt.blogspot.com On Mar 8, 2007, at 5:26 AM, Star Burger wrote:> > I''ve implemented > > class Message < ActiveRecord::Base > belongs_to :sender, :class_name => ''User'' > has_and_belongs_to_many :recipients, :class_name => ''User'' > end > > class User < ActiveRecord::Base > has_many :sent_messages, :class_name => ''Message'' > has_and_belongs_to_many :received_messages, :class_name => ''Message'' > end > > with > > CREATE TABLE users ( > id INT NOT NULL AUTO_INCREMENT, > login VARCHAR(80) NOT NULL, > ... > primary key (id), > INDEX (id) > ) engine = InnoDB; > > create table messages ( > id INT NOT NULL AUTO_INCREMENT, > user_id int not null, > subject varchar(255), > body text, > created_at DATETIME default NULL, > modified_at DATETIME default NULL, > > primary key(id) > ) engine = InnoDB, character set utf8; > > create table messages_users ( > message_id int not null, > user_id int not null, > > primary key(message_id, user_id) > ) engine = InnoDB, character set utf8; > > First everything seemed fine. But user.received_messages delivers > message objects that are DEFINITELY NOT in the database (wrong sender > ID)!! I had only one singel message in the DB, but > > user.received_messages.first yields another object than Message.find > :first > > Strange, strange!!! A bug maybe? > > -- > Posted via http://www.ruby-forum.com/. > > >--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---