0

I wonder, why I always have to also specify the has_many :assignments association in both of the models in question when using :through? Is this DRY? Are there cases when I do not need to specify them, or when they differ? Thank you for explanation.

class Programmer < ActiveRecord::Base
  has_many :projects, :through => :assignments
  has_many :assignments # Why that?
end

class Project < ActiveRecord::Base
  has_many :programmers, :through => :assignments
  has_many :assignments # Why that?
end

class Assignment < ActiveRecord::Base
  belongs_to :project
  belongs_to :programmer
end

Update

It seems I wasn't clear enough that I'm talking about has_many :through! So the answers given to this point don't really fit my question. So again:

Why do I always need a has_many :assignments when I already have a has_many :projects, :through => :assignments? Shouldn't Rails just add has_many :assignments itself automatically?

Joshua Muheim
  • 12,617
  • 9
  • 76
  • 152
  • no, rails doesn't create the connection automatically. As I said on my answer, you need to understand the reason you cannot have a many-to-may relationship. The table between them (`assignments`) is solving the many-to-many problem. You can consider the `:through => :assignments` a favour rails is doing for you to facilitate things in your API. – gabrielhilal Jul 10 '12 at 14:34
  • Thank you, I'm still not getting why Rails doesn't do this (Pseudo Code): `if has_many has :through parameter, then add has_many association`. – Joshua Muheim Jul 10 '12 at 14:49
  • Doesn't matter which relationship definition you type in first in your class definition. At runtime, rails will look for the specification of that :through relationship. Rails is very good about simplifying your code through the use of conventions, but it doesn't make anything up on its own. – railsdog Jul 10 '12 at 17:20

3 Answers3

3

I think it is important to understand the below:

You have a many-to-many relationship between Programmer and Project. In order to solve this relationship you have added a junction model called Assignment.

Actually the table projects is not linked to the programmers, both are connected to assignments instead.

You could have something like this:

class Programmer < ActiveRecord::Base
  has_many :assignments
end

class Project < ActiveRecord::Base
  has_many :assignments
end

class Assignment < ActiveRecord::Base
  belongs_to :project
  belongs_to :programmer
end

However, you don't want to access the table assignments each time you want to find all projects related to a programmer. In order to facilitate this connection, you can create a direct link between Programmer and Project. So, you can say that a programmer has many projects through the table assignments.

The result is your current set up. Without this configuration you could not for example.

#find a programmer
@programmer = Programmer(1)  
#find all projects related to this programmer
@projects = @programmer.projects

On the above example you don't need to even remember that there is a table assignments between them.

Take a look here as well: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association

I hope it helps...

EDIT

I have included a diagram, so you can visualise what I am trying to explain.

The Project and Programmer are not directly linked. So, has_many :projects, :through => :assignments is a Rails feature to facilitate thinks for you. And NO, it doesn't replace the has_many :assignments, which is the real connection to the table assignments.

Furthermore, the :through => feature can be used in other circumstances as well.

enter image description here

gabrielhilal
  • 10,660
  • 6
  • 54
  • 81
  • Thank you, this is a nice and detailed explanation. As written below, I was confused about this because Shoulda insists on both associations being present. I opened an issue on GitHub to ask the developer for the reason [here](https://github.com/thoughtbot/shoulda-matchers/issues/129). – Joshua Muheim Jul 10 '12 at 12:39
  • I have updated my question, it's about `has_many :through`, not only `has_many`! Maybe you can update your answer accordingly. :) – Joshua Muheim Jul 10 '12 at 14:06
0

The has many x through x line defines a relationship on its own. In your case it tells the Programmer model that a programmer has many projects and can find out about this relationship by looking at the assignment model. This doesn't specify a relationship with the assignment model in and of itself though. Hence why the second line is necessary.

TangoKilo
  • 1,785
  • 3
  • 25
  • 43
  • This means, that I don't need the has_many association if I don't want to do stuff like `my_programmer.assignments`? But are there cases where I don't want this association to exist anyway? Wouldn't it be nicer to have it added automatically anyway? – Joshua Muheim Jul 10 '12 at 12:00
  • Ok, after some research: According to [Shoulda](https://github.com/thoughtbot/shoulda/issues/213#issuecomment-6874448), it absolutely **is** required! I tried it out myself, and yes, it doesn't work without both associations properly specified. So there's still the question: why doesn't Rails add the `has_many :xxx` (without `:through`) automatically? – Joshua Muheim Jul 10 '12 at 14:09
0

If you want to be able to access the assignments (and it has useful data on it) from both a programmer and a project instance, then yes, you have to include that association in each.

BUT, it's not required - so, if you don't need to access assignments and it's really just a join table, then leave it out.

steakchaser
  • 5,198
  • 1
  • 26
  • 34
  • Okay, thank you. But it's interesting that Shoulda matchers in RSpec insists on an existing `has_many` relationship, otherwise a spec like `it { should have_many(:userroles).through(:usergroup_userroles) }` doesn't pass. Is Shoulda wrong, or too much opinionated then? – Joshua Muheim Jul 10 '12 at 12:04
  • According to [Shoulda](https://github.com/thoughtbot/shoulda/issues/213#issuecomment-6874448), it absolutely **is** required! I tried it out myself, and yes, it doesn't work without both associations properly specified. So there's still the question: why doesn't Rails add the `has_many :xxx` (without `:through`) automatically? – Joshua Muheim Jul 10 '12 at 14:08