2012-01-11 40 views
18

Tôi có 5 mô hình sau: Guardian, Student, Relationship, RelationshipType và School. Giữa chúng, tôi có các liên kết nàyNhận hai liên kết trong một Nhà máy để chia sẻ một liên kết khác

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 

Tôi muốn viết FactoryGirl xác định mối quan hệ. Mọi mối quan hệ phải có người giám hộ và học sinh. Hai người này phải thuộc về cùng một trường. Nhà máy giám hộ có liên kết với trường học, và nhà máy sinh viên cũng vậy. Tôi đã không thể làm cho chúng được xây dựng trong cùng một trường học. Tôi đã có đoạn mã sau:

FactoryGirl.define do 

    factory :relationship do 
    association :guardian 
    association :student, :school => self.guardian.school 
    relationship_type RelationshipType.first 
    end 

end 

Điều này dẫn đến các lỗi sau khi tôi cố gắng xây dựng một mối quan hệ sử dụng nhà máy này:

undefined method `school' for #<FactoryGirl::Declaration::Implicit:0x0000010098af98> (NoMethodError) 

Có cách nào để làm những gì tôi muốn, để làm cho người giám hộ và học sinh thuộc cùng một trường mà không cần phải nghỉ mát để vượt qua những người bảo vệ và sinh viên đã tạo ra cho nhà máy (không phải mục đích của nó)?

+0

Tôi không chắc liệu điều này có liên quan đến lỗi hay không, nhưng lớp học được viết dưới dạng khai báo lớp quan hệ thứ hai (trước khi tôi chỉnh sửa). – PinnyM

Trả lời

7

Tôi nghĩ rằng điều này sẽ làm việc:

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

Có thực sự nên là một cách tốt hơn để làm điều này ... – Ajedi32

9

Câu trả lời này là kết quả đầu tiên trên Google cho 'nhà máy cô gái chia sẻ liên kết' và câu trả lời từ santuxus thực sự giúp tôi ra :)

Dưới đây là một bản cập nhật với cú pháp từ phiên bản mới nhất của Factory Girl trong trường hợp bất kỳ ai khác tình cờ gặp phải:

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 

Điều khoản unless ngăn chặn student bị thay thế nếu nó được chuyển vào nhà máy với FactoryGirl.create(:relationship, student: foo).

2

Có cách viết sạch hơn để viết liên kết này. Câu trả lời có được từ 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

Dựa trên vấn đề có vẻ như dòng đó nên là 'sinh viên {build (: học sinh, trường học: guardian.school)}' thay vào đó ('mối quan hệ' bị bỏ qua) –

1

Đây không thực sự là câu trả lời bạn đang tìm kiếm, nhưng dường như khó khăn trong việc tạo liên kết này cho thấy rằng thiết kế bảng có thể cần được điều chỉnh.

Đặt câu hỏi What if the user changes school?, trường học trên cả StudentGuardian cần được cập nhật, nếu không, các mô hình sẽ không đồng bộ.

Tôi đưa ra một học sinh, một người giám hộ và một trường học, tất cả đều có mối quan hệ với nhau. Nếu một học sinh thay đổi trường học, một Relationship mới sẽ được tạo cho trường mới. Như một hiệu ứng phụ tốt đẹp này cho phép một lịch sử tồn tại nơi học sinh đã được học.

Các liên kết belongs_to sẽ bị xóa khỏi StudentGuardian và chuyển sang Relationship thay thế.

Nhà máy sau đó có thể được thay đổi để giống như thế này:

factory :relationship do 
    school 
    student 
    guardian 
    relationship_type 
end 

này sau đó có thể được sử dụng trong các cách sau:

# 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

Tôi muốn sử dụng transient & dependent thuộc tính trong trường hợp này :

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 

Cách sử dụng:

relationship = FactoryGirl.create(:relationship) 

relationship.guardian.school == relationship.student.school 
# => true 

Và thậm chí bạn có thể ghi đè lên các trường nếu bạn muốn:

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

Mở rộng trên giải pháp nitsas', bạn có thể lạm dụng @overrides để kiểm tra xem người giám hộ hoặc hiệp hội sinh viên đã được ghi đè, và sử dụng hiệp hội trường học từ người giám hộ/học sinh. Điều này cho phép bạn ghi đè không chỉ trường học, mà còn chỉ là người giám hộ hoặc chỉ học sinh.

Thật không may, điều này dựa trên các biến mẫu, không phải API công khai. Các bản cập nhật trong tương lai có thể phá vỡ các nhà máy của bạn.

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 
Các vấn đề liên quan