Возможно ли иметь "полиморфные отношения has_one" в рельсах? - PullRequest
37 голосов
/ 08 сентября 2011

Я бы хотел сделать что-то вроде этого:

Category
--------
- id
- name

Tag
--------
- id
- tag


Campaign
--------
- id
- name
- target (either a tag *or* a category)

Является ли полиморфная ассоциация ответом здесь?Кажется, я не могу понять, как использовать его с has_one: target,: as =>: targettable.

По сути, я хочу, чтобы Campaign.target был установлен в тег или категорию (или, возможно, другую)модель в будущем).

Ответы [ 3 ]

78 голосов
/ 08 сентября 2011

Я не верю, что вам нужна has_one ассоциация здесь, belongs_to должно быть тем, что вы ищете.

В этом случае вы захотите *Столбцы 1005 * и target_type в таблице Campaign, вы можете создать их в граблях с помощью вызова t.references :target (где t - переменная table).

class Campaign < ActiveRecord::Base
  belongs_to :target, :polymorphic => true
end

Теперь кампанию можно связать либо с Tag, либо с Category, и @campaign.target вернет соответствующий.

Ассоциация has_one будет использоваться, если выиметь внешний ключ на целевой таблице, указывающий обратно на Campaign.

Например, ваши таблицы будут иметь

Tag: id, tag, campaign_id Category: id, category, campaign_id

и будут иметьbelongs_to :campaign ассоциация на них обоих.В этом случае вам придется использовать has_one :tag и has_one :category, но вы не можете использовать универсальный target на данный момент.

Имеет ли это смысл?

РЕДАКТИРОВАТЬ

Поскольку target_id и target_type фактически являются внешними ключами для другой таблицы, ваш Campaign принадлежит одному из них.Я вижу вашу путаницу с формулировкой, потому что логически Campaign - это контейнер.Я думаю, вы можете думать об этом, как Campaign имеет одну цель, и это Tag или Container, поэтому он принадлежит к Tag или Container.

has_one это способ сказать, что отношения определены на целевом классе.Например, Tag был бы связан с кампанией через отношение has_one, так как в классе тегов нет ничего, что идентифицирует связь.В этом случае у вас будет

class Tag < ActiveRecord::Base
  has_one :campaign, :as => :target
end

и аналогично Category.Здесь ключевое слово :as сообщает рельсам, какая связь связана с этим Tag.Rails не знает, как понять это заранее, потому что нет никакой связи с именем tag на Campaign.

Другие две опции, которые могут вызвать дальнейшую путаницу, это source и source_type опции.Они используются только в отношениях :through, когда вы фактически присоединяетесь к ассоциации through другой таблицы.Документы, вероятно, описывают это лучше, но source определяет имя ассоциации, а source_type используется там, где эта ассоциация полиморфна.Их нужно использовать только тогда, когда целевая ассоциация (в классе :through) имеет имя, которое не очевидно - как в случае выше с тегом target and - и нам нужно указать rails, какое из них использовать.

6 голосов
/ 01 января 2016

Ответы на эти вопросы великолепны, но я просто хотел упомянуть другой способ добиться того же.Вместо этого вы можете создать две взаимосвязи, например:

1002

. Проверка гарантирует, что вы не получите случайно оба поля, а помощники target обеспечивают полиморфный доступ к тегу./category.

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

1 голос
/ 30 сентября 2015

Небольшое дополнение: в миграции, где вы создали таблицу Campaign, вызов t.references :target должен иметь :polymorphic => true (по крайней мере, с рельсами 4.2)

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