Получите две ассоциации внутри Factory, чтобы поделиться другой ассоциацией
У меня есть эти 5 моделей: Guardian, Student, Relationship, RelationshipType и School. Между ними у меня есть эти ассоциации
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, который определяет отношения. У каждого отношения должен быть опекун и студент. Эти двое должны принадлежать одной школе. У опекуна factory есть связь со школой, и ученик factory. Я не смог заставить их быть построенными в одной школе. У меня есть следующий код:
FactoryGirl.define do
factory :relationship do
association :guardian
association :student, :school => self.guardian.school
relationship_type RelationshipType.first
end
end
Это приводит к следующей ошибке, когда я пытаюсь построить отношения, используя этот factory:
undefined method `school' for #<FactoryGirl::Declaration::Implicit:0x0000010098af98> (NoMethodError)
Есть ли способ сделать то, что я хочу, чтобы страж и ученик принадлежали к одной и той же школе, не прибегая к передаче уже созданных опекунов и учеников к factory (что не является его целью)?
Ответы
Ответ 1
Я думаю, что это должно сработать:
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
Этот ответ является первым результатом в Google для 'factory girl shared association', и ответ от santuxus действительно помог мне:)
Здесь обновление с синтаксисом из последней версии Factory Girl в случае, если кто-то еще наткнулся на него:
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
, если оно было передано в Factory с FactoryGirl.create(:relationship, student: foo)
.
Ответ 3
Существует более чистый способ написать эту ассоциацию. Ответ получен из этой проблемы github.
FactoryGirl.define do
factory :relationship do
association :guardian
student { build(:student, school: relationship.guardian.school) }
relationship_type RelationshipType.first
end
end
Ответ 4
На самом деле это не тот ответ, который вы ищете, но кажется, что трудность в создании этой ассоциации предполагает, что дизайн таблицы может потребоваться отрегулировать.
Задавая вопрос What if the user changes school?
, необходимо обновить школу как для Student
, так и для Guardian
, иначе модели не синхронизируются.
Я предложил, чтобы студент, опекун и школа, все вместе имеют отношения. Если ученик меняет школу, для новой школы создается новый Relationship
. Как хороший побочный эффект, это позволяет истории существования того, где ученик был обучен.
Ассоциации belongs_to
будут удалены из Student
и Guardian
и вместо этого перейдут на Relationship
.
factory можно изменить таким образом:
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
Ответ 5
Я бы использовал 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
Ответ 6
Продолжая решение 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