I am new to rails. Having a blast. The query API though is giving me
some trouble. I’ve been zooming and doing a lot of stuff very quickly,
but this is the first time I have spent hours trying to figure it out.
It’s not like anything I’ve used before – regular SQL, or Hibernate,
or whatever.
The model I have is pretty simple.
- A PrivateMessage has many Recipients
- A Recipient has a Receiver (which of class User)
- recipient also has fields for ‘is_read’ and ‘is_deleted’
My goal is to build a query that finds all the unread and not deleted
private messages for a given user. To accomplish this, we need to join
‘private_messages’ to ‘recipients’… and then ‘recipients’ to
‘users’.
Here’s the relevant User model code:
has_many :sent_messages, :class_name => 'PrivateMessage', :foreign_key => 'sender_id'
has_many :recipient_of_messages, :class_name => 'Recipient', :foreign_key => 'receiver_id'
scope :by_id, lambda { |id| where(:id => id) }
My Recipient model has the following relevant code:
belongs_to :receiver, :class_name => 'User', :foreign_key => "receiver_id"
belongs_to :private_message
scope :unread, where(:is_read => false).where(:is_deleted => false)
scope :by_receiver_id, lambda { |id| Recipient.joins(:receiver).merge(User.by_id(id)) }
scope :unread_by_receiver_id, lambda { |id| unread.by_receiver_id(id) }
When tested in isolation, this works 100%.
However, when I code the private message queries, I run into problems.
belongs_to :sender, :class_name => 'User'
has_many :recipients, :class_name => 'Recipient'
scope :sorted, order("private_messages.created_at desc")
scope :non_deleted, where(:is_deleted_by_sender => false)
scope :non_deleted_by_sender_id, lambda { |id| sorted.non_deleted.joins(:sender).merge(User.by_id(id)) }
# this scope does not work
scope :non_deleted_by_receiver_id, lambda { |id| sorted.joins(:recipients).merge(Recipient.by_receiver_id(id)) }
scope :newest, sorted.limit(3)
# this scope does not work either
scope :newest_unread_by_receiver_id, lambda { |id| newest.joins(:recipients).merge(Recipient.unread_by_receiver_id(id)) }
When I try and use ‘newest_unread_by_receiver_id’ or ‘non_deleted_by_receiver_id’, I get the following exception:
ActiveRecord::ConfigurationError: Association named 'receiver' was not found; perhaps you misspelled it?
This doesn’t make much sense to me… because if the name was spelled
wrong, why doesn’t it fail when I test it isolation?
Can someone help me out please? This one is driving me nuts. At times
like this, I just want to program in full sql or Hibernate QL so I could just be done with it 🙁
If I’m just approaching the problem totally wrong, then I’d appreciate it if you just let me know that too. I am under the impression that using scopes and ActiveRelation was the way moving forward in Rails 3.1.
Thanks
I would probably use something like this. I kept scopes separate for clarity.
Models (renamed PrivateMessage -> Message and Recipient -> MessageCopy):
Schema (migrations would have taken too much space here):
–edit
Example query using joins returning messages
Message scope reusing scopes on other model
–edit2
This Railscast has nice examples of both joins and scopes: