Подтвердить что ты не робот

Модель Rails has_many с несколькими foreign_keys

Относительно новый для рельсов и попытка моделировать очень простое семейное "дерево" с одной моделью 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 внешними ключами или, возможно, изменить внешний ключ на основе пола объекта? Или есть другой/лучший способ вообще?

Спасибо!

4b9b3361

Ответ 1

Нашел простой ответ на 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

Ответ 2

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

def children
   children_of_mother.merge(children_of_father)
end

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

Ответ 3

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

Используется 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

Ответ 5

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

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

Ответ 6

Не решение общего вопроса, как указано ( "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

Ответ 7

Я искал одну и ту же функцию, если вы не хотите возвращать массив, но 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

Ответ 8

Мой ответ на Ассоциации и (несколько) внешних ключей в рельсах (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

Итак, любые вопросы?