Модель Rails has_many с несколькими иностранными ключами - PullRequest
48 голосов
/ 21 ноября 2008

Относительно плохо знаком с рельсами и пытается смоделировать очень простое семейное «дерево» с единственной моделью Person, которая имеет имя, пол, Father_id и mother_id (2 родителя). Ниже в основном то, что я хочу сделать, но, очевидно, я не могу повторить: children в has_many (первое перезаписывается).

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

Есть ли простой способ использовать has_many с 2 внешними ключами или, возможно, изменить внешний ключ в зависимости от пола объекта? Или вообще есть другой / лучший способ?

Спасибо!

Ответы [ 8 ]

43 голосов
/ 21 ноября 2008

Нашел простой ответ по IRC, который, кажется, работает (благодаря 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
16 голосов
/ 16 августа 2014

Чтобы улучшить ответ Кензи , вы можете достичь отношения ActiveRecord, определив Person#children как:

def children
   children_of_mother.merge(children_of_father)
end

см. этот ответ для более подробной информации

9 голосов
/ 27 марта 2010

Использовал named_scopes для модели Person сделать это:

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
8 голосов
/ 21 ноября 2008

Я верю, что вы можете достичь желаемых отношений, используя: 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

Я подтвердлю и отредактирую этот ответ после работы; )

4 голосов
/ 09 мая 2013

Я предпочитаю использовать области для этой проблемы. Как это:

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

Этот трюк облегчает получение детей без использования экземпляров:

Person.children_for father_id, mother_id
3 голосов
/ 04 ноября 2016

Мой ответ на Связи и (несколько) внешних ключей в рельсах (3.2): как описать их в модели и записать миграции только для вас!

Что касается вашего кода, вот мои модификации

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

Так есть вопросы?

3 голосов
/ 12 декабря 2014

Не решение общего вопроса, как указано («has_many с несколькими внешними ключами»), но, учитывая, что человек может быть либо матерью, либо отцом, но не обоими, я бы добавил столбец gender и пошел бы с

  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 голосов
/ 07 июня 2016

Я искал ту же функцию, если вы не хотите возвращать массив, кроме ActiveRecord::AssociationRelation, вы можете использовать << вместо +. ( См. Документацию ActiveRecord )

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