has_many:在Rails中使用has_and_belongs_to_many

时间:2012-02-23 07:39:38

标签: ruby-on-rails ruby-on-rails-3 activerecord associations model-associations

在Rails中 - 使用has_many的效果是什么:通过has_and_belongs_to_many? 考虑使用两个模型 - 具有多对多关系的帖子和标签,如下所示:

class Tag < ActiveRecord::Base
  has_many :posts_tag
  has_and_belongs_to_many :posts
end

class Post < ActiveRecord::Base
  has_many :posts_tag
  has_many :tags,  :through => posts_tag
end

class PostsTag < ActiveRecord::Base
  belongs_to :tag
  belongs_to :post
end

我使用has_and_belongs_to_many的原因是因为tag属于许多帖子。

我确实调查了Rails Association guide并看到他们没有提到这种多对多关系的情况。但是,我确实试过这个并且在Rails中运行它没有产生任何行为,并且从我构建的小测试数据库中,似乎也返回了post.tagstag.posts的正确结果 - 其中{ {1}}和post分别指代tagPost模型的实例。

这是正确的用法还是有任何我不知道的副作用?另外,如果它是正确的,这是Rails实现这个目标的方法吗?

谢谢!

2 个答案:

答案 0 :(得分:2)

仅在设置多对多关联时使用has_and_belongs_to_many(换句话说,当另一方也有has_and_belongs_to_many时)。这就是这种联想的意义。

你应该

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

请注意,我使用了复数post_tags(因为这是正确的方法)。

如果你有评论中的情况,你应该有

belongs_to :post_tag

Post模型中,

has_many :posts

PostTag模型中。

您现在可以问:“我为什么要使用belongs_to :post_tag属于标签, 标签。所以,不应该我用has_one :post_tag?“起初这也是我的问题,但后来我认为Rails不能总是完全适合英语。您需要post_tag_id上的post列,而belongs_to则需要这一列。另一方面,has_one会在其他一侧出现名为post_id的列,即post_tag。但这是不可能的,因为post_tag有很多posts(不仅仅是一个),因此post ID无法保存在post_tags中。

<强>更新
关联之间的区别仅在于您提供的方法和可以传递的选项(Rails指南中关于关联的解释)。例如,has_onebelongs_to具有相同的方法:

association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})

但是,例如,方法association=create_association意味着外键应该在哪里(就像我上面解释的那样)。

has_and_belongs_to_manyhas_many的方法可能没有任何不同,但它们可以通过的选项不同。例如,您可以传入

:dependent => :destroy
<{1>}关联上的

,但你无法将其传递给has_many,因为这意味着没有多大意义,因为它意味着多对多关联;如果父记录被销毁,子记录仍然可以与其他记录连接,因此它们也不应被销毁。

答案 1 :(得分:1)

虽然我不确定在关系的一侧有has_many :through和在另一侧有has_and_belongs_to_many的确切影响,但我知道更正确的方法是像这样使用反向has_many :through

class Tag < ActiveRecord::Base
  has_many :posts_tag
  has_many :posts,  :through => posts_tag
end

保持其他关系不变。