2008-11-21 12 views
47

Relativamente nuevo en rails e intentando modelar un "árbol" familiar muy simple con un único modelo Person que tiene un nombre, género, father_id y mother_id (2 padres). Debajo está básicamente lo que quiero hacer, pero obviamente no puedo repetir lo siguiente: niños en un has_many (el primero se sobrescribe).Rails Modelo has_many con multiple foreign_keys

class Person < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Person' 
    belongs_to :mother, :class_name => 'Person' 
    has_many :children, :class_name => 'Person', :foreign_key => 'mother_id' 
    has_many :children, :class_name => 'Person', :foreign_key => 'father_id' 
end 

¿Hay una manera simple de usar has_many con 2 llaves extranjeras, o tal vez cambiar la clave externa basada en el género del objeto? ¿O hay otra/mejor manera en conjunto?

Gracias!

+0

Para Rails 3, encadenamiento de ámbito, ActiveRecord :: Relación y, finalmente, 'has_many': http://stackoverflow.com/questions/17476521/rails-has-many-custom-activerecord-association/17476639#17476639 – MrYoshiji

+0

Estás buscando "claves compuestas": http: // stackoverflow.com/questions/17882105/is-it-possible-to-defined-composite-primary-key-for-table-using-active-record – xpepermint

Respuesta

43

encontrado una respuesta simple en el IRC que parece funcionar (gracias a Radar):

class Person < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Person' 
    belongs_to :mother, :class_name => 'Person' 
    has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id' 
    has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id' 
    def children 
    children_of_mother + children_of_father 
    end 
end 
+20

pero #niños devuelven una matriz ... –

+1

Si hay ámbitos predeterminados, en particular, aquellos que pueden afectar el orden de los resultados, esta solución no funcionará ya que los resultados no se ordenan como se esperaba. – mgadda

+0

Funciona. ¡Gracias! – Abram

8

Creo que puede lograr las relaciones que desea utilizando: has_one.

class Person < ActiveRecord::Base 
    has_one :father, :class_name => 'Person', :foreign_key => 'father_id' 
    has_one :mother, :class_name => 'Person', :foreign_key => 'mother_id' 
    has_many :children, :class_name => 'Person' 
end 

Confirmaré y editaré esta respuesta después del trabajo;)

+0

Esto no funcionó para mí ... parecía bueno para ser cierto, pero Obtuve el erorr esperado para la relación 'has_many': 'ninguna columna llamada' person_id' en la tabla 'people'. – deivid

8

named_scopes utilizar en el modelo de persona hacer esto:

class Person < ActiveRecord::Base 

    def children 
     Person.with_parent(id) 
    end 

    named_scope :with_parent, lambda{ |pid| 

     { :conditions=>["father_id = ? or mother_id=?", pid, pid]} 
    } 
end 
4

Yo prefiero usar alcances para este problema De esta manera:

class Person < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Person' 
    belongs_to :mother, :class_name => 'Person' 
    has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id' 
    has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id' 

    scope :children_for, lambda {|father_id, mother_id| where('father_id = ? AND mother_id = ?', father_id, mother_id) } 
end 

Este truco hace que sea fácil para que los niños sin casos de uso:

Person.children_for father_id, mother_id 
17

Para mejorar la respuesta Kenzie 's, se puede lograr una relación de ActiveRecord definiendo Person#children como:

def children 
    children_of_mother.merge(children_of_father) 
end 

ver this answer para más detalles

+0

ADVERTENCIA: Como se explica en la respuesta que menciona, las relaciones se combinan con un 'Y '. Lo cual no funciona con este ejemplo, porque significa que selecciona solo personas que tienen 'mother_id' * y * (en lugar de * o *)' father_id' establecido en el id del objetivo. No soy médico, pero no debería ser tan frecuente :) –

+0

.merge trabajó en mi caso en el que necesitaba AND. ¡Gracias! – Vlad

3

No es una solución a la pregunta general tal como se establece ("has_many with multiple foreign keys"), pero dado que una persona puede ser madre o padre, pero no ambas, agregaría una columna gender e iré con

has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id' 
    has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id' 
    def children 
    gender == "male" ? children_of_father : children_of_mother 
    end 
2

Estaba buscando la misma función, si no desea devolver una matriz sino ActiveRecord::AssociationRelation, puede usar << en lugar de +. (See the ActiveRecord documentation)

class Person < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Person' 
    belongs_to :mother, :class_name => 'Person' 

    has_many :children_of_father, :class_name => 'Person', :foreign_key => 'father_id' 
    has_many :children_of_mother, :class_name => 'Person', :foreign_key => 'mother_id' 

    def children 
    children_of_mother << children_of_father 
    end 
end 
2

Mi respuesta a Associations and (multiple) foreign keys in rails (3.2) : how to describe them in the model, and write up migrations es sólo para ti!

cuanto a su código, aquí están mis modificaciones

class Person < ActiveRecord::Base 
    belongs_to :father, :class_name => 'Person' 
    belongs_to :mother, :class_name => 'Person' 
    has_many :children, ->(person) { unscope(where: :person_id).where("father_id = ? OR mother_id = ?", person.id, person.id) }, class_name: 'Person' 
end 

Así alguna pregunta?

+0

¡Esto es asombroso! Hay varias respuestas incorrectas a la pregunta sobre StackOverflow, pero esto funciona perfectamente. Corrección rápida sin embargo: mal escrito 'mother_id'. – KurtPreston

+0

gracias por su apoyo. Lo estoy editando ahora mismo. – sunsoft

+0

Esta respuesta me ayudó, esta debería ser la correcta ... –

Cuestiones relacionadas