In Rails - what is the effect of using has_many :through with has_and_belongs_to_many? Consider having two models - Posts and Tags which have a many-to-many relationship as indi
While I'm not sure of the exact effects of having a has_many :through
on one side of a relationship and a has_and_belongs_to_many
on the other side, I do know that the more correct way, would be to use a reversed has_many :through
like so:
class Tag < ActiveRecord::Base
has_many :posts_tag
has_many :posts, :through => posts_tag
end
Keeping the other relationships the same.
You use has_and_belongs_to_many
only when you're setting a many-to-many association (in other words, when the other side also has has_and_belongs_to_many
). That is the meaning of this association.
You should have
class Tag < ActiveRecord::Base
has_many :posts_tags
has_many :posts, :through => :post_tags
end
class PostsTag < ActiveRecord::Base
belongs_to :tag
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :posts_tags
has_many :tags, :through => :posts_tags
end
Notice that I used the plural, post_tags
(because this is the correct way).
If you have the situation like in your comment, you should have a
belongs_to :post_tag
in your Post
model, and
has_many :posts
in your PostTag
model.
You may ask now: "Why should I use belongs_to :post_tag
? It doesn't belong to a tag, it has a tag. So, shouldn't I use has_one :post_tag
?". This was also my question at first, but then I figured that it Rails cannot always perfectly suit the english language. You need the post_tag_id
column on your post
, and belongs_to
expects exactly that. On the other hand, has_one
would expect that a column named post_id
is present on the other side, that is in your post_tag
. But this would be impossible, because post_tag
has many posts
(not only one), so the post
IDs cannot be held in post_tags
.
Update:
The difference between associations are only in the methods you are provided and options you can pass in (the one explained in the Rails guide on associations). For example, has_one
and belongs_to
have the same methods:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
But, for example, methods association=
and create_association
imply different things concerning where the foreign key should be (like I explained above).
has_and_belongs_to_many
and has_many
probably don't have anything different in their methods, but they differ in the options you can pass. For example, you can pass in
:dependent => :destroy
on the has_many
association, but you can't pass it to a has_and_belongs_to_many
, because that wouldn't make sense, since it implies a many-to-many association; if a parent record is destroyed, child records can still be connected with other records, so they shouldn't also be destroyed.