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

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

Спасибо!

Для Rails 3, цепочка областей видимости, ActiveRecord :: Relation и в конечном итоге has_many: stackoverflow.com/questions/17476521/…

MrYoshiji 04.07.2013 23:02

Вы ищете "составные ключи": stackoverflow.com/questions/17882105/…

xpepermint 27.02.2014 16:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
50
2
23 816
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

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

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

Это не сработало для меня ... казалось, что это правда, но я получил ожидаемый erorr для отношения has_many: 'нет столбца с именем person_id в таблице people.

deivid 21.02.2014 14:48
Ответ принят как подходящий

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

Если есть какие-либо области по умолчанию, в частности те, которые могут повлиять на порядок результатов, это решение не будет работать, так как результаты не будут упорядочены, как ожидалось.

mgadda 09.08.2012 21:54

вы можете получить ActiveRecord:Relation, указав #children, как я сделал в своем ответе ниже

stevenspiel 16.08.2014 22:11

Разве это не вызовет два SQL-запроса? Если вы захотите добавить больше связей, это может стать довольно неэффективным.

stefvhuynh 23.11.2014 01:37

ВНИМАНИЕ: с Rails 4.2 (и, вероятно, со всем, что было до этого), вызов children запускает два запроса SQL (один для children_of_father и один для children_of_mother). С большим набором данных это не будет эффективно. @stefvhuynh, спасибо, что подняли вопрос.

philippe_b 17.02.2016 18:57

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

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

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

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

def children
   children_of_mother.merge(children_of_father)
end

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

ПРЕДУПРЕЖДЕНИЕ. Как объясняется в упомянутом вами ответе, отношения объединяются с помощью AND. Что не работает с этим примером, потому что это означает, что вы выбираете только людей, у которых mother_idи (вместо или же) father_id установлен на идентификатор цели. Я не врач, но это не должно случаться так часто :)

philippe_b 17.02.2016 19:02

.merge работал в моем случае, где мне нужно было AND. Спасибо!

Vlad 04.05.2016 13:47

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

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

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

Есть вопросы?

Это круто! На вопрос о StackOverflow есть несколько неправильных ответов, но это отлично работает. Однако быстрое исправление: вы неправильно написали mother_id.

KurtPreston 13.12.2016 10:27

@KalleSamuelsson благодарит вас за поддержку! Люблю ваш комментарий!

sunsoft 19.10.2017 06:05

Другие вопросы по теме