通过`has_many through:`关联添加对象的正确方法是什么?

时间:2013-11-28 12:49:41

标签: ruby-on-rails ruby-on-rails-4 has-many-through has-many

我想更好地理解has_many through:关联如何在Rails 4.0上运行。

假设我有这些模型:

class Parent
  has_many :parent_sons
  has_many :sons, through: :parent_sons
end

class Son
  has_many :parent_sons
  has_many :parents, through: :parent_sons
end

class ParentSon
  belongs_to :parent
  belongs_to :son
  validates :son, presence: true
  validates :parent, presence: true
end

现在,如果我进入控制台并创建几个测试对象,通过has_many through:定义关联父母和儿子,我会得到:

a = Parent.new
b = Son.new
a.sons << b
a.valid?
=> false

# The ParentSon object has a Son but no Parent:
c = a.parent_sons.first
c.son
=> b
c.parent
=> nil

如果我输入

,就会出现同样的行为
a.parent_sons.build(son: b)

而不是a.sons << b

所以,显然,ParentSon对象和Son对象之间已创建了正确的关联,而ParentSonParent之间没有创建正确的关联。将Son附加到。

同样,如果我这样做

b.parents << a

我将获得ParentSonParent之间的关系,但不会获得原始Son之间的关系。

显然这打破了ParentSon对象中的验证,因为两个外向关系中的一个总是丢失。

那么,在不破坏此类验证的情况下,使用has_many through:关联附加现有对象的正确方法是什么?

修改

这种行为也打破了我的rspec测试。以这种方式使用factory_girl gem:

factory :parent do
  after(:create) do |p|
    p.sons << FactoryGirl.create(:son)
  end
end

通过FactoryGirl.create(:parent)创建的对象不会因同样的原因通过验证,因为它的ParentSon对象具有正确的Son引用但空Parent

2 个答案:

答案 0 :(得分:1)

我发现在使用has_many through:时,使用现有Parent对象正确添加与Son的关系的唯一方法是完全定义连接对象的详细信息。

例如

a.parent_sons.build(parent: a, son: b)

是正确的方法。对父级createbuild儿子的任何其他尝试都会导致parent_id对象中缺少ParentSon

通过这种关联,a.sons << b方法是不可行的。

答案 1 :(得分:0)

我认为您的问题是由您在尚未创建对象时尝试设置关联(即尚未拥有ID)引起的。这是怎么回事?

#make an unsaved (no id) Parent
a = Parent.new
#make an unsaved (no id) Son
b = Son.new

#try to make the association, by building a join table (ParentSon) record.  
a.sons << b

#the ParentSon object has neither a parent_id or a son_id, which is making it fail validation
a.valid?
=> false

如果要在尚未创建的对象之间建立关联,可以使用.build代替。这将在保存父项时保存父项时的关联对象。例如

parent = Parent.new
son = parent.sons.build 
parent.save

父亲现在有一个id,儿子有一个id,并且应该建立关联。两者都应该有效。试试吧。