Understanding :source option of has_one/has_many through of Rails
Please help me in understanding the :source
option of has_one/has_many :through
association. The Rails API explanation makes very little sense to me.
"Specifies the source association name used by
has_many
:through => :queries
. Only use it if the name cannot be inferred from the association.has_many :subscribers, :through => :subscriptions
will look for either:subscribers
or:subscriber
onSubscription
, unless a:source
is given. "
Solution 1:
Sometimes, you want to use different names for different associations. If the name you want to use for an association on the model isn't the same as the assocation on the :through
model, you can use :source
to specify it.
I don't think the above paragraph is much clearer than the one in the docs, so here's an example. Let's assume we have three models, Pet
, Dog
and Dog::Breed
.
class Pet < ActiveRecord::Base
has_many :dogs
end
class Dog < ActiveRecord::Base
belongs_to :pet
has_many :breeds
end
class Dog::Breed < ActiveRecord::Base
belongs_to :dog
end
In this case, we've chosen to namespace the Dog::Breed
, because we want to access Dog.find(123).breeds
as a nice and convenient association.
Now, if we now want to create a has_many :dog_breeds, :through => :dogs
association on Pet
, we suddenly have a problem. Rails won't be able to find a :dog_breeds
association on Dog
, so Rails can't possibly know which Dog
association you want to use. Enter :source
:
class Pet < ActiveRecord::Base
has_many :dogs
has_many :dog_breeds, :through => :dogs, :source => :breeds
end
With :source
, we're telling Rails to look for an association called :breeds
on the Dog
model (as that's the model used for :dogs
), and use that.
Solution 2:
Let me expand on that example:
class User
has_many :subscriptions
has_many :newsletters, :through => :subscriptions
end
class Newsletter
has_many :subscriptions
has_many :users, :through => :subscriptions
end
class Subscription
belongs_to :newsletter
belongs_to :user
end
With this code, you can do something like Newsletter.find(id).users
to get a list of the newsletter's subscribers. But if you want to be clearer and be able to type Newsletter.find(id).subscribers
instead, you must change the Newsletter class to this:
class Newsletter
has_many :subscriptions
has_many :subscribers, :through => :subscriptions, :source => :user
end
You are renaming the users
association to subscribers
. If you don't provide the :source
, Rails will look for an association called subscriber
in the Subscription class. You have to tell it to use the user
association in the Subscription class to make the list of subscribers.
Solution 3:
Most simple answer:
Is the name of the relationship in the table in the middle.