2012-01-11 39 views
18

我有以下5個模型:監護人,學生,關係,關係類型和學校。在他們之間,我有這些協會在工廠內部獲得兩個關聯以共享另一個關聯

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

我不確定這是否與錯誤有關,但School類是作爲第二個關係類聲明(編輯之前)編寫的。 – PinnyM 2012-01-11 23:02:34

回答

7

我認爲這應該工作:

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 
+3

真的應該有這樣做的更好的方法... – Ajedi32 2012-08-15 14:06:19

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)傳入工廠,則不會被替換。

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 
+0

基於這個問題,它看起來應該是'student {build(:student,school:guardian.school)}''而不是'(關係) – 2017-06-10 15:12:46

1

這不是真的你正在尋找一個答案,但似乎在創造這個協會的難度表明該表的設計可能需要調整。

問題What if the user changes school?StudentGuardian需要更新,否則模型不同步。

我提出一個學生,一個監護人和一個學校,都有關係。如果學生更換學校,則爲新學校創建一個新的Relationship。作爲一個很好的副作用,這使歷史能夠存在於學生已經受過教育的地方。

belongs_to關聯將從StudentGuardian中刪除,而是移至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 
0

我想在這種情況下使用transient & 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 
0

上nitsas'解決方案來擴展,你可以濫用@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