Multiple relations between two models

I currently have two models. These are user and message.

Message belongs to user and user has many messages. I have set the
relation up so I can do something like the following…

#code
current_user.messages
##end code

This gets a list of all of the incoming messages for the logged user.
This works just fine.

What I really want to do, is to be able to say something like…

#code
message.sender
##end code

and have this return a model of the user that sent the message. What’s
the best way to achieve this if I already have one relation? I have a
field in the user model that is set up to record the user ID of the
person who sends the message. I am just not sure how to express the
relationship in rails using active record.

Any help would be greatly appreciated.

Well i thought about this issue too sometime … and though i’m
perfectly sure, this could work:

class Message < ActiveRecord::Base
belongs_to :sender, :foreign_key => “sender_id”, :class => “User”
belongs_to :recipient, :foreign_key => “recipient_id”, :class =>
“User”
end

class User < ActiveRecord::Base
has_many :sentmessages, :foreign_key => “sender_id”, :class =>
“Message”
has_many :rcvdmessages, :foreign_key => “recipient_id”, :class =>
“Message”
end

Then you should be able to do:

somevariable = message.sender
somevariable = message.recipient

sentmsgs = user.sentmessages.find :all
rcvdmsgs = user.rcvdmessages.find :all

So you practically reference the other class twice in both Models, but
with different assiciation names (:sender <-> :recipient and
:sentmessages <-> :rcvdmessages) and foreig keys ( sender_id <->
recipient_id).
That creates two different associations with the same Model …
Someone please correct me if i’m wrong, i never tested it and only made
my own thoughts from the rails docs

As i want to use soemthing like this myself soon, and IF the above is
correct, i want to ask:

How is the best way to store the messages twice, so sender + recipient
can both keep a copy and delete it whenever they want?
Anyone got any idea?

On 10/18/06, Stewart [email protected] wrote:

the best way to achieve this if I already have one relation? I have a
field in the user model that is set up to record the user ID of the
person who sends the message. I am just not sure how to express the
relationship in rails using active record.

You can use the dynamic finder:

message.find_sender_by_id( :id => userID )

This is the simplest way to find the sender object.

Thorsten L wrote:

Well i thought about this issue too sometime … and though i’m
perfectly sure, this could work:

class Message < ActiveRecord::Base
belongs_to :sender, :foreign_key => “sender_id”, :class => “User”
belongs_to :recipient, :foreign_key => “recipient_id”, :class =>
“User”
end

class User < ActiveRecord::Base
has_many :sentmessages, :foreign_key => “sender_id”, :class =>
“Message”
has_many :rcvdmessages, :foreign_key => “recipient_id”, :class =>
“Message”
end

Then you should be able to do:

somevariable = message.sender
somevariable = message.recipient

sentmsgs = user.sentmessages.find :all
rcvdmsgs = user.rcvdmessages.find :all

So you practically reference the other class twice in both Models, but
with different assiciation names (:sender <-> :recipient and
:sentmessages <-> :rcvdmessages) and foreig keys ( sender_id <->
recipient_id).
That creates two different associations with the same Model …
Someone please correct me if i’m wrong, i never tested it and only made
my own thoughts from the rails docs

As i want to use soemthing like this myself soon, and IF the above is
correct, i want to ask:

How is the best way to store the messages twice, so sender + recipient
can both keep a copy and delete it whenever they want?
Anyone got any idea?

i like your suggestion. Bala what your suggesting is easy and thats what
i was going to go with but that would require more code than i would
like in the view…

I worked it out with the help of the api

class Message < ActiveRecord::Base
belongs_to :user
validates_presence_of :title, :body
belongs_to :sender, :class_name => “User”, :foreign_key => “user_id”
end

thats all i had to do. For the record the hash is :class_name not :class

Also… the first hash you pass the belongs_to method must match the key
in the current model your dealing with.

So problem solved thanks for that…

I looked in to it a bit more and i am finding you can do a lot with the
model… such as this,

has_many :users, :order => “last_name”

now when ever i list the users its always in alpha…

how easy is that!

Stewart wrote:

both the sender and the userid are set to the wrong values in the
database. The sender always has a value of 0 in the database. The
userid always has a value of 1.

Hang on. You should only have one column in your ‘messages’ table that
relates to this join, and that column should be ‘user_id’. You
shouldn’t have a column that called ‘sender’.

As far as I can tell, that code should work fine, as long as
current_user contains a saved User object.

When you say “The sender always has a value of 0 in the database. The
userid always has a value of 1.”, exactly which tables and columns are
you talking about?

Chris

Stewart wrote:

Thorsten L wrote:

Well i thought about this issue too sometime … and though i’m
perfectly sure, this could work:

class Message < ActiveRecord::Base
belongs_to :sender, :foreign_key => “sender_id”, :class => “User”
belongs_to :recipient, :foreign_key => “recipient_id”, :class =>
“User”
end

class User < ActiveRecord::Base
has_many :sentmessages, :foreign_key => “sender_id”, :class =>
“Message”
has_many :rcvdmessages, :foreign_key => “recipient_id”, :class =>
“Message”
end

Then you should be able to do:

somevariable = message.sender
somevariable = message.recipient

sentmsgs = user.sentmessages.find :all
rcvdmsgs = user.rcvdmessages.find :all

So you practically reference the other class twice in both Models, but
with different assiciation names (:sender <-> :recipient and
:sentmessages <-> :rcvdmessages) and foreig keys ( sender_id <->
recipient_id).
That creates two different associations with the same Model …
Someone please correct me if i’m wrong, i never tested it and only made
my own thoughts from the rails docs

As i want to use soemthing like this myself soon, and IF the above is
correct, i want to ask:

How is the best way to store the messages twice, so sender + recipient
can both keep a copy and delete it whenever they want?
Anyone got any idea?

i like your suggestion. Bala what your suggesting is easy and thats what
i was going to go with but that would require more code than i would
like in the view…

I worked it out with the help of the api

class Message < ActiveRecord::Base
belongs_to :user
validates_presence_of :title, :body
belongs_to :sender, :class_name => “User”, :foreign_key => “user_id”
end

thats all i had to do. For the record the hash is :class_name not :class

Also… the first hash you pass the belongs_to method must match the key
in the current model your dealing with.

So problem solved thanks for that…

I looked in to it a bit more and i am finding you can do a lot with the
model… such as this,

has_many :users, :order => “last_name”

now when ever i list the users its always in alpha…

how easy is that!

It’s possible I am not as smart as they think I am.

class Message < ActiveRecord::Base
belongs_to :sender, :class_name => “User”, :foreign_key => “user_id”
end

the above line of code works fine for me when I do something like
this…

<%= message.sender.full_alpha_name %>

however, when I am trying to create a new message with the following
code…

def sendmessage
@message = Message.new()

@message.body = params[:message][:body]
@message.title = params[:message][:title]
@message.user_id = params[:message][:to]
@message.sender = current_user

if @message.save
  flash[:notice] = "Message Sent"
  redirect_to :action => "index"
else
  flash[:notice] = "Message not Sent"
  redirect_to :action => "index"
end

end

both the sender and the userid are set to the wrong values in the
database. The sender always has a value of 0 in the database. The
userid always has a value of 1.

I have checked the form variables and they are fine.

When I comment out the following line everything works correctly

#belongs_to :sender, :class_name => “User”, :foreign_key => “user_id”

Am I missing something here? Do I need to do something in my user model
apart from

has_many :messages

thanks in advance for any help.

Thorsten L wrote:

i think you got something twisted up.
-> in the model, you say, the sender’s foreign key is “user_id”
-> in the controller, you assign the recipient ( params[:message][:to])
to the column “user_id”

From what you described i guess you have a foreign_key “sender” in your
message table that shold hold the ID of the user who sent the message,
and the “user_id” filed should hold the name of the recipient of the
message
Your column naming could be more self-explanatory, e.g. use
“recipient_id” as i suggested in my above post

i think, your :belongs_to sould look like this:

belongs_to :sender, :class_name “User”, :foreign_key => “sender”
belongs_to :recipient, :class_name “User”, :foreign_key => “user_id”

but you should give some more info, like your exact table layout etc

and what do the parameters hold? i guess params[:message][:to] hold the
recipients name. well you need that user’s id, not his name.

But im just guessing here, not enough insight and info…

Ok sorry i should have posted this stuff beofre… here we go

class CreateUsers < ActiveRecord::Migration
def self.up
create_table “users”, :force => true do |t|
t.column :login, :string
t.column :email, :string
t.column :crypted_password, :string, :limit => 40
t.column :salt, :string, :limit => 40
t.column :created_at, :datetime
t.column :updated_at, :datetime
t.column :remember_token, :string
t.column :remember_token_expires_at, :datetime
t.column :first_name, :string, :null => false
t.column :last_name, :string, :null => false
t.column :organization_id, :integer, :null => false
t.column :title, :string
t.column :location, :string
end
end

def self.down
drop_table “users”
end
end

class CreateMessages < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.column :title, :string, :null => false
t.column :body, :text, :null => false
t.column :user_id, :integer, :null => false
t.column :sender, :integer, :null => false
t.column :created_at, :datetime, :null => false
t.column :read_at, :datetime

end

end

def self.down
drop_table :messages
end
end

the sender column in the message table stores the user_id of the user
sending the message

the user_id in the message table contains the id number of the user that
is the recipient (I will later change this to recipient_id its a better
name)

params[:message][:to] is the id of the user that the message is being
set to. The code looks like this…

To
<%= collection_select("message", "to" , current_user.organization.users, "id", "full_alpha_name" ) %>

its all working now though i just needed to fix up my keys and add in
that other belongs_to…

Thanks soo much for that guys!

i think you got something twisted up.
-> in the model, you say, the sender’s foreign key is “user_id”
-> in the controller, you assign the recipient ( params[:message][:to])
to the column “user_id”

From what you described i guess you have a foreign_key “sender” in your
message table that shold hold the ID of the user who sent the message,
and the “user_id” filed should hold the name of the recipient of the
message
Your column naming could be more self-explanatory, e.g. use
“recipient_id” as i suggested in my above post

i think, your :belongs_to sould look like this:

belongs_to :sender, :class_name “User”, :foreign_key => “sender”
belongs_to :recipient, :class_name “User”, :foreign_key => “user_id”

but you should give some more info, like your exact table layout etc

and what do the parameters hold? i guess params[:message][:to] hold the
recipients name. well you need that user’s id, not his name.

But im just guessing here, not enough insight and info…

Did you ever get this worked out? I think I’m having a very similar
problem. Quite maddening.

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/21e5e6cc5292b0ff/43362f8d56e33e16#43362f8d56e33e16

Thanks,
Jason