has_one with two foreign keys? - ruby-on-rails

I have two classes Message and User. Message has sender_id and recipient_id both foreign keys for User. How to build relationship where I'll be able to get user for both sender and recipient, like #message.sender.name and #message.recipient.name
I tried to do it by this way:
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => 'User', :foreign_key => 'sender'
belongs_to :recipient, :class_name => 'User', :foreign_key => 'recipient'
end
class User < ActiveRecord::Base
has_many :recivied_messages, :class_name => 'Message', :foreign_key => 'recipient'
has_many :send_messages, :class_name => 'Message', :foreign_key => 'sender'
end
But it didn't help, when I'm trying to access to, for instance, #message.recipient.name it says that "undefined method `name'"

You can use the :class_name property to set which class gets used for a foreign key:
class Message < ActiveRecord::Base
has_one :sender, :class_name => User
has_one :recipient, :class_name => User
end
class User < ActiveRecord::Base
belongs_to :sent_messages, :class_name => Message
belongs_to :received_messages, :class_name => Message
end
Also, you say you are using sender_id and recipient_id for the foreign keys, but in your code you have :foreign_key => 'sender' and :foreign_key => 'recipient'. Have you tried changing them to :foreign_key => 'sender_id' and :foreign_key => 'recipient_id'? So:
class Message < ActiveRecord::Base
has_one :sender, :class_name => User, :foreign_key => 'sender_id'
has_one :recipient, :class_name => User, :foreign_key => 'recipient_id'
end
class User < ActiveRecord::Base
belongs_to :sent_messages, :class_name => Message, # ...etc
belongs_to :received_messages, :class_name => Message, # ...etc
end

Related

mongoid: message&reply: Is this a good structure?

I have to implement a message model which would have replies for itself. I ended up something like this:
class Message
include Mongoid::Document
belongs_to :sender, :class_name => "User", :inverse_of => :snt_msg
belongs_to :recipient, :class_name => "User", :inverse_of => :rcvd_msg
embeds_many :replies, :class_name => "Message"
embedded_in :message, :inverse_of => :replies
end
and this for user:
class User
include Mongoid::Document
has_many :snt_msg, :class_name => 'Message', :inverse_of => :sender
has_many :rcvd_msg, :class_name => 'Message', :inverse_of => :recipient
end
Is that Ok to work with it, or is there any well-structure design for that?

How to set up relations between these two rails models

I have 2 models: User and PrivateMessage which must be associated (as user has many private messages as reciever and sender, private messages belongs to user)
It's my private_messages table structure:
private_messages:
sender_id:integer
reciever_id:integer
title:string
message:text
It's hard for me to understand how can I connect same message for both sender user and reciever user, now my models code looks like:
class User < ActiveRecord:Base
has_many :private_messages
end
and
class PrivateMessage < ActiveRecord::Base
belongs_to :user, :through => :sender_id
belongs_to :user, :through => :reciever_id
end
Is that correct?
You have to rename your associations to tell them apart:
class PrivateMessage < ActiveRecord::Base
belongs_to :sender, :class_name => 'User', :foreign_key => 'sender_id'
belongs_to :receiver, :class_name => 'User', :foreign_key => 'receiver_id'
end
class User < ActiveRecord::Base
has_many :sent_messages, :class_name => 'PrivateMessage', :foreign_key => 'sender_id', :dependent => :destroy
has_many :received_messages, :class_name => 'PrivateMessage', :foreign_key => 'receiver_id', :dependent => :destroy
end

rails way user to user messaging, do I need a join table?

Quick question (I think). I have users, and I would like to allow them to send messages to one another. My question is, should I have a user table, a message table, and a users to messages join table, or should I just store to_user_id and from_user_id in the messages table. If the latter, what would the association look like for that? Can you even reference a 'local' key for an association?
You can do that with a couple of simple has_many associations. Since it's self-referential, you'll need to override some of the Rails magic to make it work.
class User < ActiveRecord::Base
has_many :sent_messages, :class_name => 'Message', :foreign_key => 'sender_id'
has_many :received_messages, :class_name => 'Message', :foreign_key => 'recipient_id'
end
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
Rails doesn't have a cleaner way to doing self-referential associations that I know of.
I think the latter sounds fine. This is just off the top of my head but I know AR's associations have options like this...
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => :user, :foreign_key => :from_user_id
belongs_to :recipient, :class_name => :user, :foreign_key => :to_user_id
#...
end
class User < ActiveRecord::Base
has_many :received_messages, :class_name => :message, :foreign_key => :to_user_id
has_many :sent_messages, :class_name => :message, :foreign_key => :from_user_id
end

Rails associations

I am trying to make a private messaging system, where you send a message to one or more users. Now the messages schema is like
user_id, to_id, msg
How do i make the associations so that i can get the details of the sender and the recipient?
schema:
sender_id
recipient_id
class Message
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
class User
has_many :sent_messages, :class_name => 'Message', :foreign_key => :sender_id
has_many :income_messages, :class_name => 'Message', :foreign_key => :recipient_id
end

Rails Model has_many with multiple foreign_keys

Relatively new to rails and trying to model a very simple family "tree" with a single Person model that has a name, gender, father_id and mother_id (2 parents). Below is basically what I want to do, but obviously I can't repeat the :children in a has_many (the first gets overwritten).
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children, :class_name => 'Person', :foreign_key => 'mother_id'
has_many :children, :class_name => 'Person', :foreign_key => 'father_id'
end
Is there a simple way to use has_many with 2 foreign keys, or maybe change the foreign key based on the object's gender? Or is there another/better way altogether?
Thanks!
Found a simple answer on IRC that seems to work (thanks to Radar):
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
children_of_mother + children_of_father
end
end
To improve on Kenzie's answer, you can achieve an ActiveRecord Relation by defining Person#children like:
def children
children_of_mother.merge(children_of_father)
end
see this answer for more details
Used named_scopes over the Person model
do this:
class Person < ActiveRecord::Base
def children
Person.with_parent(id)
end
named_scope :with_parent, lambda{ |pid|
{ :conditions=>["father_id = ? or mother_id=?", pid, pid]}
}
end
I believe you can achieve the relationships you want using :has_one.
class Person < ActiveRecord::Base
has_one :father, :class_name => 'Person', :foreign_key => 'father_id'
has_one :mother, :class_name => 'Person', :foreign_key => 'mother_id'
has_many :children, :class_name => 'Person'
end
I'll confirm and edit this answer after work ; )
My answer to Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations is just for you!
As for your code,here are my modifications
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children, ->(person) { unscope(where: :person_id).where("father_id = ? OR mother_id = ?", person.id, person.id) }, class_name: 'Person'
end
So any questions?
I prefer to use scopes for this issue. Like this:
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
scope :children_for, lambda {|father_id, mother_id| where('father_id = ? AND mother_id = ?', father_id, mother_id) }
end
This trick make it easy to get children without use instances:
Person.children_for father_id, mother_id
Not a solution to the general question as stated ("has_many with multiple foreign keys"), but, given a person can either be a mother or a father, but not both, I would add a gender column and go with
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
gender == "male" ? children_of_father : children_of_mother
end
I was looking for the same feature, if you don't want to return an array but a ActiveRecord::AssociationRelation, you can use << instead of +.
(See the ActiveRecord documentation)
class Person < ActiveRecord::Base
belongs_to :father, :class_name => 'Person'
belongs_to :mother, :class_name => 'Person'
has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id'
has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id'
def children
children_of_mother << children_of_father
end
end

Resources