Rails associations - ruby-on-rails

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

Related

How to << a Join Model with attributes to a collection

I have a User model which joins to another User model through a UserRelationship join table. UserRelationships have an attribute (approved/pending/revoked) which must be set, but does not default to any option. There are two associations which reflect this, traveler and delegate, so my models look like this:
User.rb
has_many :traveler_relationships, :class_name => 'UserRelationship', :foreign_key => :delegate_id
has_many :travelers, :class_name => 'User', :through => :traveler_relationships
has_many :delegate_relationships, :class_name => 'UserRelationship', :foreign_key => :user_id
has_many :delegates, :class_name => 'User', :through => :delegate_relationships
has_many :buddy_relationships, class_name: 'UserRelationship', foreign_key: :user_id
has_many :buddies, class_name: 'User', through: :buddy_relationships, source: :delegate
UserRelationship.rb
belongs_to :relationship_status
belongs_to :traveler, :class_name => 'User', :foreign_key => 'user_id'
belongs_to :delegate, :class_name => 'User'
PENDING = 1
CONFIRMED = 3
REVOKED = 5
I'm trying to write up some specs where one user is related to another and the simplest way to write it would be #user1.travelers << #user2 but this fails the database constraint that UserRelationship.relationship_status not be null.
When I try #user1.buddies.create(delegate: #user2, relationship_status: RelationshipStatus::CONFIRMED), it fails saying UnknownAttributeError on delegate. I looked at this question and tried its solution, using attr_acessible, but it didn't change the UnknownAttributeError.
What is the way to create this join record with an attribute set?
I assume UserRelationship has a foreign_key delegate_id for the delegate
class User < ActiveRecord::Base
has_many :traveler_relationships, :class_name => 'UserRelationship', :foreign_key => :user_id
has_many :delegate_relationships, :class_name => 'UserRelationship', :foreign_key => :delegate_id
has_many :travelers, :class_name => 'User', :through => :traveler_relationships
has_many :delegates, :class_name => 'User', :through => :delegate_relationships
end
class UserRelationship < ActiveRecord::Base
belongs_to :traveler, :class_name => 'User', :foreign_key => :user_id
belongs_to :delegate, :class_name => 'User', :foreign_key => :delegate_id
PENDING = 1
CONFIRMED = 3
REVOKED = 5
end
I dont think you can use the shortcut #user1.delegates << #user2 since you need to specify relationship status. Try:
#user1.traveler_relationships.create(delegate: #user2, relationship_status: RelationshipStatus::CONFIRMED)
I've excluded the buddy synonym here. It's complex enough as is. When this works you can look into adding synonyms.
You're on the right track when you try to create from buddies (I'm assuming that's your join model relation). The problem is that your column name is relationship_status and you were passing status to create. Try this:
#user1.buddies.create(delegate: #user2, relationship_status: RelationshipStatus::CONFIRMED

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 do I use Rails to find one has_many but not the other?

Message has_many user_messages. It has two.
1 UserMessage will have the sender as user_id
The other UserMessage will have the receiver as user_id
Knowing one user, how can I, for any message, find the other?
Here's what I tried as a method to the Message class, but it fails:
32 def other_user(one_user)
33 um = self.user_messages
34 um.each do |user_message|
35
36 output_user = User.find(user_message.user_id) unless user_message.user_id == one_user.id
37 end
38
39 return output_user
40 end
This could be implemented as an association enhancement. This will be efficient for high-volume of messages as it pushes processing to DB.
class User < ActiveRecord::Base
has_many :sent_messages, :class_name => "Message", :foreign_key => "sender_id"
has_many :receivers, :through => :sent_messages, :source => :receiver do
def for_message(message_id)
where("messages.id = ?", message_id)
end
end
# Another association mapping for received_messages
end
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => "User", :foreign_key => "sender_id"
belongs_to :receiver, :class_name => "User", :foreign_key => "receiver_id"
end
#Usage
User.first.sent_messages
#all users received messages from first user
User.first.receivers
#all users received messages from first user for message of message_id
User.first.receivers.for_message(message_id)
It seems like there should be a better definition of who is the sender and who is the receiver, but in the meantime, what about:
def other_user(one_user)
user_messages.reject do |msg|
msg.user_id == one_user.id
end.first.user
end
It takes the set of user_messages, filters out the one that belongs to one_user. There should be one left in the array, so pick it out and return the user attached to it.
EDIT
I might not understand all your requirements, but I'd probably just add a sender_id and receiver_id directly to Message and remove the UserMessage join table. Then you could define the relationship like:
class User
has_many :sent_messages, :class_name => 'Message', :foreign_key => 'sender_id'
has_many :received_messages, :class_name => 'Message', :foreign_key => 'receiver_id'
end
class Message
belongs_to :sender, :class_name => 'User'
belongs_to :recipient, :class_name => 'User'
end
EDIT 2
If you want the intermediate table for tracking user-specific states, you could look into using STI to subclass UserMessage as SentMessageLink and ReceivedMessageLink (those names are not awesome -- but you get the idea), then you could use has_many :through.
class SentMessageLink < UserMessage
belongs_to :sender, :class_name => 'User'
belongs_to :message
end
class User
has_many :sent_message_links
has_many :sent_messages, :through => :sent_message_links
end
class Message
has_one :sent_message_link
has_one :sender, :through => :sent_message_link
end
with similar code for received messages. Then you should be able to access sender and receiver right from the message.

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

has_one with two foreign keys?

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

Resources