鉴于Foo
和Bar
模型之间存在one-to-one
关系并且彼此存在验证:
class Foo < ActiveRecord::Base
has_one :foo_bar
has_one :bar, through: :foo_bar, inverse_of: :foo
validates :bar, presence: true
end
class FooBar < ActiveRecord::Base
belongs_to :foo
belongs_to :bar
end
class Bar < ActiveRecord::Base
has_one :foo_bar
has_one :foo, through: :foo_bar, inverse_of: :bar
validates :foo, presence: true
end
他们的工厂:
FactoryGirl.define do
factory :foo do
foo_bar
bar { |foo| build(:bar, foo_bar: foo_bar, foo: foo) }
end
factory :bar do
foo_bar
foo { |bar| build(:foo, foo_bar: foo_bar, bar: bar) }
end
factory :foo_bar do
end
end
当我尝试使用Foo
创建Bar
或FactoryGirl.create(:foo)
的实例时,我得到SystemStackError: stack level too deep
。
原因是:
foo
对象时,会查看关联的bar
bar
未保存,我们需要先保存bar
时,它会查看foo
(尚未保存)和
决定保存它一种解决方案是在关联的一端添加autosave: false
,如:
class Bar
has_one :foo, through: :foo_bar, inverse_of: :bar, autosave: false
end
这允许创建Foo
,但会阻止create(:bar)
,因为不会保存foo
关联,从而导致无效Bar
记录(因为验证)。
如何解决这个问题,以便foo
和bar
工厂都有效?
答案 0 :(得分:1)
这是一个概念性问题。考虑一下 - 你在国外,你丢失了你的文件。你去你的大使馆,他们想看到你的机票签发新护照。你去航空公司,他们希望你的护照签发机票副本。这是一个僵局,一个鸡与蛋的问题。
解决问题的唯一方法是重新定义它。您可以决定要验证关系,但至少在其中一个方向上,您只有在创建后才会进行验证。
class Foo < ActiveRecord::Base
has_one :foo_bar
has_one :bar, through: :foo_bar, inverse_of: :foo
validates :bar, presence: true, on: :update
end
因此,在我们的例子中,航空公司仍然不会在没有护照的情况下向您发放您的机票,但是大使馆会在没有机票的情况下给您护照。此外,如果您已经拥有护照并因任何原因前往大使馆,他们会要求查看门票以确认。
答案 1 :(得分:0)
我认为validates :foo, presence: true
最终会使用validates_associated
。
文档说明:不要在关联的两端使用validates_associated。他们会在无限循环中互相称呼。
http://guides.rubyonrails.org/active_record_validations.html#validates-associated
基本上,一个人必须是追随者...也许你可以解释一下你的用例并得到一些数据模型反馈?
答案 2 :(得分:0)
我同意模型结构似乎没有了。最好的解决方案是通过不同的关联可能达到你想要的效果。但是,如果这不是一个选项,我会抛出一个可能让你到处找到的想法。
您可以覆盖自动保存并自行检查状态。你仍然需要依赖另一个,但你可以在你的Foo模型中做这样的事情:
def autosave_associated_records_for_bar
#validate data
#save bar
#probably need to save ids on foobar
end
注意:执行此操作时应小心,确保您了解效果(验证器可能无法运行等)
您可以在此处的源代码中查看有关AutosaveAssociation的更多信息:https://github.com/rails/rails/blob/master/activerecord/lib/active_record/autosave_association.rb
也许那里的东西会有所帮助。