我有这五个模型:卫报,学生,关系,关系类型和学校。在他们之间,我有这些关联
class Guardian < ActiveRecord::Base
belongs_to :school
has_many :relationships, :dependent => :destroy
has_many :students, :through => :relationships
end
class Student < ActiveRecord::Base
belongs_to :school
has_many :relationships, :dependent => :destroy
has_many :guardians, :through => :relationships
end
class Relationship < ActiveRecord::Base
belongs_to :student
belongs_to :guardian
belongs_to :relationship_type
end
class School < ActiveRecord::Base
has_many :guardians, :dependent => :destroy
has_many :students, :dependent => :destroy
end
class RelationshipType < ActiveRecord::Base
has_many :relationships
end
我想写一个定义关系的FactoryGirl。每个关系都必须有一个监护人和一个学生。这两个人必须属于同一所学校。监护人工厂与学校有联系,学生工厂也是如此。我一直无法让他们在同一所学校建造。我有以下代码:
FactoryGirl.define do
factory :relationship do
association :guardian
association :student, :school => self.guardian.school
relationship_type RelationshipType.first
end
end
当我尝试使用此工厂构建关系时,会导致以下错误:
undefined method `school' for #<FactoryGirl::Declaration::Implicit:0x0000010098af98> (NoMethodError)
有没有办法做我想做的事,让监护人和学生属于同一所学校,而不必诉诸已经创建的监护人和学生到工厂(这不是它的目的)?
答案 0 :(得分:9)
这个答案是谷歌“工厂女孩共享协会”的第一个结果,而santuxus的回答确实帮助了我:)
以下是有关其他人偶然发现的最新版工厂女孩的语法更新:
FactoryGirl.define do
factory :relationship do
guardian
relationship_type RelationshipType.first
after(:build) do |relationship|
relationship.student = FactoryGirl.create(:student, school: relationship.guardian.school) unless relationship.student.present?
end
end
end
unless
子句阻止student
被FactoryGirl.create(:relationship, student: foo)
传递到工厂时被替换。
答案 1 :(得分:8)
我认为这应该有效:
FactoryGirl.define do
factory :relationship do
association :guardian
relationship_type RelationshipType.first
after_build do |relationship|
relationship.student = Factory(:student, :school => relationship.guardian.school)
end
end
end
答案 2 :(得分:2)
有更清晰的方式来编写这种关联。答案来自this github issue。
FactoryGirl.define do
factory :relationship do
association :guardian
student { build(:student, school: relationship.guardian.school) }
relationship_type RelationshipType.first
end
end
答案 3 :(得分:1)
这不是您正在寻求的答案,但似乎创建此关联的困难表明可能需要调整表格设计。
提出问题What if the user changes school?
,Student
和Guardian
上的学校需要更新,否则模型会不同步。
我提出一个学生,一个监护人和一个学校,都有一个关系。如果学生改变学校,则为新学校创建新的Relationship
。作为一个很好的副作用,这使得学生在某个学习的地方存在历史。
belongs_to
关联将从Student
和Guardian
中移除,而是移至Relationship
。
然后可以将工厂更改为:
factory :relationship do
school
student
guardian
relationship_type
end
然后可以通过以下方式使用它:
# use the default relationship which creates the default associations
relationship = Factory.create :relationship
school = relationship.school
student = relationship.student
guardian = relationship.guardian
# create a relationship with a guardian that has two charges at the same school
school = Factory.create :school, name: 'Custom school'
guardian = Factory.create :guardian
relation1 = Factory.create :relationship, school: school, guardian: guardian
relation2 = Factory.create :relationship, school: school, guardian: guardian
student1 = relation1.student
student2 = relation2.student
答案 4 :(得分:1)
延伸到nitsas&#39;解决方案,您可以滥用@overrides
来检查监护人或学生协会是否被覆盖,并使用监护人/学生的学校协会。这使您不仅可以覆盖学校,还可以覆盖监护人或学生。
不幸的是,这依赖于实例变量,而不是公共API。未来的更新很可能打破你的工厂。
factory :relationship do
guardian { create(:guardian, school: school) }
student { create(:student, school: school) }
transient do
school do
if @overrides.key?(:guardian)
guardian.school
elsif @overrides.key?(:student)
student.school
else
create(:school)
end
end
end
end
答案 5 :(得分:0)
我使用transient&amp;在这种情况下dependent属性:
FactoryGirl.define do
factory :relationship do
transient do
school { create(:school) }
# now you can even override the school if you want!
end
guardian { create(:guardian, school: school) }
student { create(:student, school: school) }
relationship_type RelationshipType.first
end
end
用法:
relationship = FactoryGirl.create(:relationship)
relationship.guardian.school == relationship.student.school
# => true
如果你愿意,你甚至可以覆盖学校:
awesome_school = FactoryGirl.create(:school)
awesome_relationship = FactoryGirl.create(:relationship, school: awesome_school)
awesome_relationship.guardian.school == awesome_school
# => true
awesome_relationship.student.school == awesome_school
# => true