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开发者_Go百科)
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.
精彩评论