Настройка отношений для пары записей в одной модели - PullRequest
1 голос
/ 22 декабря 2011

У меня есть таблица items, и мне нужно создать несколько отдельных пар. Моя схема содержит equivalent_id, в котором хранится идентификатор партнера, если этот партнер существует.

Каков наилучший способ установить отношения в модели? Кажется странным сказать has_one или belongs_to, потому что ни один из элементов в паре не является концептуально доминирующим над другим.

Ответы [ 2 ]

1 голос
/ 22 декабря 2011

Самореференциальная и двунаправленная ассоциация «один-к-одному» (на удивление) немного сложнее, чем самореференциальная и двунаправленная ассоциация «один-ко-многим» или даже «многие-ко-многим».

Просто чтобы прояснить:

  • Самостоятельная ссылка: связанный элемент является экземпляром того же класса, что и его владелец.
  • двунаправленный: если элемент является эквивалентом другого, то последний также является эквивалентом первого упомянутого.

Это значит:

revolver = Item.create name: 'revolver'
pistol   = Item.create name: 'pistol'

revolver.equivalent = pistol
revolver.equivalent # => pistol
pistol.equivalent   # => revolver

Другими словами, когда вы устанавливаете эквивалентный элемент для элемента, также должен быть установлен эквивалентный идентификатор владельца.

Ассоциация "один-к-одному" не принимает опции insert_sql и delete_sql. Так что это немного менее красиво. Тем не менее, самый чистый способ сделать это (IMO):

class Item < ActiveRecord::Base
  has_one :equivalent, class_name: 'Item', foreign_key: 'equivalent_id'

  def add_equivalent(other)
    self.equivalent  = other
    other.equivalent = self
  end

  def remove_equivalent
    equivalent.equivalent = nil
    self.equivalent       = nil 
  end
end

Таким образом, вы можете сделать:

revolver = Item.create name: 'revolver'
pistol   = Item.create name: 'pistol'

revolver.add_equivalent(pistol)
revolver.equivalent # => pistol
pistol.equivalent   # => revolver

pistol.remove_equivalent
pistol.equivalent   # => nil
revolver.equivalent # => nil

редактировать

Чтобы быть в безопасности, вы должны очищать отношения каждый раз, когда добавляете эквивалент. Это значит:

revolver.equivalent # => pistol
pistol.equivalent   # => revolver

revolver.add_equivalent(gun)
revolver.equivalent # => gun
pistol.equivalent   # => nil

Вы можете сделать это так:

def add_equivalent(other)
  remove_equivalent if equivalent
  ...
end
1 голос
/ 22 декабря 2011

Используя has_one, вы могли бы сказать has_one :equivalent, :class_name => "Item", и он выглядит для меня читабельным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...