Получить две ассоциации внутри фабрики, чтобы разделить другую ассоциацию - PullRequest
19 голосов
/ 11 января 2012

У меня есть 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)

Есть ли способ сделать то, что я хочу, чтобы опекун и ученик принадлежали к одной школе, не прибегая к передаче уже созданных опекунов и учеников на фабрику (что не является ее целью)?

Ответы [ 6 ]

9 голосов
/ 26 января 2013

Этот ответ является первым результатом в Google для «Общение с девушками на фабрике», и ответ от 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, если она была передана на завод с FactoryGirl.create(:relationship, student: foo).

8 голосов
/ 14 января 2012

Я думаю, что это должно работать:

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 голосов
/ 05 октября 2015

Есть более чистый способ написания этой ассоциации. Ответ получен от этого вопроса github .

FactoryGirl.define do
  factory :relationship do 
    association :guardian
    student { build(:student, school: relationship.guardian.school) }
    relationship_type RelationshipType.first
  end
end
1 голос
/ 18 января 2018

Распространяясь на решение 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
1 голос
/ 08 февраля 2016

Это на самом деле не тот ответ, который вы ищете, но кажется, что сложность в создании этой ассоциации предполагает необходимость корректировки дизайна таблицы.

Если задать вопрос 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
0 голосов
/ 27 января 2017

Я бы использовал переходный & зависимые атрибуты в этом случае:

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
...