2012-01-11 14 views
18

Tengo estos 5 modelos: Guardián, Estudiante, Relación, Tipo de relación y Escuela. Entre ellos, tengo estas asociacionesObtener dos asociaciones dentro de una fábrica para compartir otra asociación

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 

Quiero escribir un FactoryGirl que defina una relación. Cada relación debe tener un tutor y un estudiante. Estos dos deben pertenecer a la misma escuela. La fábrica de guardias tiene una asociación con la escuela, y también lo hace la fábrica de estudiantes. No he podido lograr que se construyan en la misma escuela. Tengo el siguiente código:

FactoryGirl.define do 

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

end 

Esto da como resultado el siguiente error cuando intento de construir una relación mediante esta fábrica:

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

¿Hay alguna manera de hacer lo que quiero, hacer que el tutor y el estudiante pertenezcan a la misma escuela sin tener que recurrir a pasar a los guardianes y estudiantes ya creados a la fábrica (¿cuál no es su propósito)?

+0

No estoy seguro de si esto tiene algo que ver con el error, pero la clase de la Escuela fue escrito como una segunda declaración de la clase Relación (antes de mi edición). – PinnyM

Respuesta

7

creo que esto debería funcionar:

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

Realmente debería haber una mejor manera de hacerlo ... – Ajedi32

9

Esta respuesta es el primer resultado en Google para 'fábrica chica compartida asociación' y la respuesta de santuxus realmente me ayudó :)

Aquí es una actualización con la sintaxis de la última versión de la chica de la fábrica en caso de cualquier otra persona tropieza a través de ella:

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 

la cláusula impide unlessstudent de ser reemplazado si se ha pasado a la fábrica con FactoryGirl.create(:relationship, student: foo).

2

Hay una forma más clara de escribir esta asociación. La respuesta fue de 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

Basado en el problema, parece que esa línea debería ser 'student {build (: student, school: guardian.school)}' en su lugar (se omite 'relationship') –

1

Esto no es realmente una respuesta que está buscando, pero parece que la dificultad en la creación de esta asociación es lo que sugiere que puede ser necesario ajustar el diseño de la tabla.

Haciendo la pregunta What if the user changes school?, la escuela tanto en el Student como en el Guardian necesita ser actualizada, de lo contrario, los modelos se desincronizarán.

Propongo que un estudiante, un tutor y una escuela, todos tengan una relación juntos. Si un estudiante cambia de escuela, se crea un nuevo Relationship para la nueva escuela. Como un buen efecto colateral, esto permite que exista una historia de donde el estudiante ha sido educado.

Las asociaciones belongs_to se eliminarán de Student y Guardian, y se moverán a Relationship.

La fábrica continuación, se puede cambiar a este aspecto:

factory :relationship do 
    school 
    student 
    guardian 
    relationship_type 
end 

Esto puede ser utilizado de la siguiente manera:

# 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

que haría uso de transient & dependent atributos en este caso :

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 

Uso:

relationship = FactoryGirl.create(:relationship) 

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

e incluso se puede anular la escuela si desea:

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

Extendiéndose sobre solución nitsas', se puede abusar de @overrides para comprobar si el tutor o asociación de estudiantes se han anulado, y el uso de la asociación escolar del tutor/alumno. Esto le permite anular no solo a la escuela, sino también al tutor o simplemente al alumno.

Desafortunadamente, esto depende de las variables de instancia, no de la API pública. Las futuras actualizaciones podrían romper sus fábricas.

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 
Cuestiones relacionadas