Reputation: 156364
Our Rails 3 application has models Person
and Message
. Messages can be specific to a Person (when the message person_id
column is set) or they can be "global" (when the person_id
column is NULL).
We would like to have a simple has_many
relationship using the :conditions
option as such:
class Person < ActiveRecord::Base
has_many :messages,
:conditions => proc { ['(messages.person_id IS NULL) OR ' +
'(messages.person_id = ?)'], self.id }
# ...
end
But it appears that the has_many
class method encodes the "conditions" option as a logical "AND" clause after enforcing the foreign key constraint of equality to the Person object's id (e.g. "FROM messages WHERE person_id=123 AND (person_id IS NULL OR person_id=123)
"). It appears that there is no way to allow associated objects with null foreign keys to belong to such associations.
Does Rails 3 / ActiveRecord provide a way to do this or must I hack my own association-like methods?
Upvotes: 4
Views: 4143
Reputation: 12643
You can't have an OR clause like you want using conditions on the ActiveRecord assocation. You could have the association without conditions and then add a method to include the global messages you want. That way you could still take advantage of the association when building associated records.
# person.rb
has_many :messages
def all_messages
Message.where('messages.person_id=? OR messages.person_id IS NULL', id)
end
Here's my standard plug for the Squeel gem, which is handy for more advanced queries like this if you don't want to have bits of SQL in your code. Check it out.
Upvotes: 1
Reputation: 7304
I think you're correct, you could forgo has_many, and create a scope with an outer join, something like:
class Person < ActiveRecord::Base
scope :messages lambda {
joins("RIGHT OUTER Join messages on persons.id = messages.person_id")
.where('persons.id = ?',self.id)
}
Upvotes: 0