RoR: has_one "или другой"? (Или полиморфизм без наследования.) - PullRequest
7 голосов
/ 17 декабря 2008

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

Что мне нужно выяснить, это что-то вроде следующего.

# 1. Foo never belongs to anything.
# 2. Foo MUST have one assigned sub-record for validity.
# 3. Foo can only have either Bar or Baz assigned.
# 4. Bar and Baz have only ONE common property, and aren't
#    related in either data or implementation.

class Foo < ActiveRecord::Base
  # Attributes: id, name, value
  has_one :assignment, :foreign_key => 'assigned_to', :readonly => true
          # Could really use an :object_type for has_one here...
end

class Bar < ActiveRecord::Base
  # Attributes: name,...
end

class Baz < ActiveRecord::Base
  # Attributes: name,...
end

Где Foo имеет одно присваивание типа Bar или Baz; они имеют только один общий столбец, так что, возможно, из этого я смогу создать родительский объект. Однако, если я заставлю их наследовать от общего объекта (когда данные, которые они содержат, на самом деле являются апельсинами и яблоками), я должен составить таблицу для записи? Могу ли я сойти с рук, если запись является абстрактной, а дети - нет?

Полагаю, вы уже можете видеть мои трудности. Я довольно новичок в RoR, но люблю его до сих пор. Я уверен, что есть способ обойти это, но я буду проклят, если не смогу понять, что это такое.

Ответы [ 2 ]

8 голосов
/ 17 декабря 2008

Вы пытаетесь смоделировать что-то, что не соответствует парадигме реляционной базы данных. Все ссылки в SQL имеют одно происхождение и одну цель.

FWIW, Полиморфные ассоциации также являются анти-паттернами, поскольку нарушают это правило. Это должно быть подсказкой, что это неправильный дизайн, когда в документации сказано, что вы должны отказаться от ограничения ссылочной целостности, чтобы оно заработало!

Вам нужно, чтобы у Foo было два has_one отношения : один с Баром, а другой с Базом. Затем реализуйте некоторую логику класса, чтобы обеспечить заполнение только одной ссылки в любом экземпляре Foo. То есть из ссылок на Bar и Baz у одного должно быть значение, а у другого должно быть ноль, но это ваш код должен проверять и применять.

0 голосов
/ 17 декабря 2008

Возможно, один из способов сделать это состоит в том, чтобы создать ассоциации «есть один» в Foo, для Bar и Baz. Затем создайте метод с именем назначением и назначением =, который может быть единственным способом доступа к Bar и Baz. Вы можете проверить, какой из двух has_ones не равен nil в методе get, и вернуть его. В методе присваивания вы можете проверить тип передаваемой переменной и установить правильное отношение has-one к этому объекту, а для другого установить значение nil. Это должно охватывать все ваши базы, не будучи слишком сложным.

...