2010-02-08 21 views
6

que tienen dos clases que me gustaría para especificar la siguiente manera:múltiples asociaciones con el mismo modelo

class Club < ActiveRecord::Base 
    belongs_to :president, :class_name => "Person", :foreign_key => "president_id" 
    belongs_to :vice_president, 
      :class_name => "Person", 
      :foreign_key => "vice_president_id" 
end 

class Person < ActiveRecord::Base 
    has_one :club, :conditions => 
    ['president_id = ? OR vice_president_id = ?', '#{self.id}', '#{self.id}'] 
end 

Esto no funciona y me da un error al intentar conseguir la asociación del club del objeto persona. El error es porque está buscando person_id en la tabla del club cuando miré el SQL. Puedo evitarlo declarando múltiples asociaciones de has_one, pero siento que esta es la forma incorrecta de hacerlo.

Una persona solo puede ser el presidente o vicepresidente de un club.

Cualquiera que sea capaz de ofrecer un poco de consejo sobre este tema, estaría muy agradecido.

Respuesta

8

Su condición has_one nunca funcionará en los carriles, por lo que yo sé.

Necesita una explícita has_one o belongs_to o has_many por "enlace", en ambas tablas. Entonces, si tiene dos "enlaces", necesita dos has_one y dos belongs_to. Así es como funciona.

En segundo lugar, creo que debería reconsiderar sus modelos. La forma en que lo hace, una persona no puede ser el presidente de un club y un empleado, al mismo tiempo. O ser el presidente de dos clubes. Incluso si no los tiene ahora mismo, pueden venir en el futuro; es más fácil mantenerse flexible en este momento.

Una forma flexible de hacerlo es usando un has_many :through con una tabla intermedia que especifica el rol. En otras palabras:

# The memberships table has a person_id, club_id and role_id, all integers 

class Membership < ActiveRecord::Base 
    belongs_to :club 
    belongs_to :person 
    validates_presence_of :role_id 
    validates_numericality_of :role_id 
end 

class Club < ActiveRecord::Base 
    has_many :memberships, :dependent => :delete_all 
    has_many :people, :through => :memberships 
end 

class Person < ActiveRecord::Base 
    has_many :memberships, :dependent => :delete_all 
    has_many :clubs, :through => :memberships 
end 

Ahora, suponiendo que los empleados ROLE_ID = 0 significa, ROLE_ID = 1 significa que el presidente, y ROLE_ID = 2 significa vice_president, se puede utilizar de esta manera:

tyler = Person.find(1) # person_id is 1 
other = Person.find(2) # person_id is 2 
c = Club.find(1) # club_id is 1 

tyler.clubs # returns all the clubs this person is "member" of 
c.people # returns all the "members" of this club, no matter their role 

#make tyler the president of c 
tyler.memberships.create(:club_id => 1, :role_id => 1) 

#make other the vicepresident of c 
#but using c.memberships instead of other.memberships (works both ways) 
c.memberships.create(:person_id => 2, :role_id => 1) 

#find the (first) president of c 
c.memberships.find_by_role_id(1).person 

#find the (first) vicepresident of c 
c.memberships.find_by_role_id(2).person 

#find all the employees of c 
c.memberships.find_all_by_role_id(0).collect { |m| m.person } 

#find all the clubs of which tyler is president 
tyler.memberships.find_all_by_role_id(1).collect { |m| m.club } 

Notas adicionales :

  • Puede complementar esto con una tabla y un modelo de roles. Los roles tendrían solo un nombre, los roles serían relaciones have_many y las membresías serían belong_to rol.O bien, puede definir métodos en membresías para obtener el nombre del rol (si es 0, devuelve "empleado", si 1, "presidente", etc.
  • Puede agregar validaciones en membresías para que no se pueda hacer más de 1 persona presidente de un club determinado o del mismo empleado en el mismo club dos veces. Más adelante, si comienza a obtener "casos excepcionales" en los que una persona necesita estar en dos lugares, deberá adaptar sus validaciones.
+0

Ok, definitivamente parece una mejor estructura. Sin embargo, no estoy seguro de cómo funciona el empleado en esto. La designación de los empleados es realmente independiente si forman parte de un club o no, por lo que no creo que deba ser parte de la membresía. A propósito, ya uso un modelo de Roles para los diferentes roles de empleados que tenemos. Entonces, ¿tal vez en el modelo Person debería haber una asociación con un has_one EmployeeRole y eso daría su tipo de empleado? – adimitri

+0

¡Hola! No estoy seguro de entender lo que quieres decir con "realmente independiente si son parte de un club o no". Si tiene una tabla Roles con roles de empleado, podría agregar 2 más ("presidente" y "vicepresidente") y solo usar una cosa. ¿Por qué quieres separarlos? – kikito

+0

Esta aplicación es para una organización gubernamental estudiantil en un campus universitario. Financian una gran cantidad de clubes que son administrados por estudiantes y tienen 4 puestos en el tablero electrónico (presidente, vicepresidente, secretario tesorero). Los empleados son del gobierno estudiantil y también estudiantes, pero no tiene nada que ver con el club. Entonces, pueden ser parte de una junta electrónica del club y un empleado. – adimitri

0

Creo que sus asociaciones son incorrectas. A su manera, es difícil asignar un presidente o vicepresidente.

lo haría así:

class Club < ActiveRecord::Base 
    has_one :president, 
      :class_name => "Person", 
      :foreign_key => 'president_club_id' 
    has_one :vice_president, 
      :class_name => "Person", 
      :foreign_key => 'vice_president_club_id' 
end 

class Person < ActiveRecord::Base 
    belongs_to :club 
end 

Ahora puede asignar los papeles como este:

club.president = Person.create(:name => 'Tom') 
club.vice_president = Person.create(:name => 'Andrew') 
+0

Eso no funcionaría porque entonces necesitaría tener un president_club_id y vice_president_club_id en la tabla People (y en mi aplicación real también tengo dos posiciones más). Además, tendría que tener varios belongs_to en la clase Person para ese para trabajar. Además, un poco de gente Es posible que no esté a cargo de los clubes, sino de los "empleados", por lo que me gustaría mantener a foreign_key fuera de su modelo. – adimitri

0

Sugiero se introduce un nuevo modelo llamado papel. Luego, tenga lo siguiente:

class Club 
    has_many :roles 

    def president 
    end 

    def vice_president 
    end 

end 

class Person 
    belongs_to :role 
end 

class Role 
    has_one :person 
    belongs_to :club 
end 
0

Este es el caso de uso clásico para asociaciones polimórficas. Aquí está el enlace: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Algo así como ..

class Club < ActiveRecord::Base 
    belongs_to :person, :polymorphic => true 

class Person < ActiveRecord::Base 
    has_one :club, :as => :position 
+0

Las asociaciones polimórficas no ayudarían a resolver el problema aquí porque se accede a varias personas desde el mismo modelo. Las asociaciones polimórficas son para cuando su foreign_key puede ser diferentes modelos. – adimitri

Cuestiones relacionadas