Избегание полиморфных ассоциаций в рельсах
(извините за плохой английский)
Предположим, у меня есть модели A, B, C. Каждая модель имеет один адрес.
В книге "Антипаттерны SQL: предотвращение ловушек программирования баз данных" (глава 7 - Полиморфные ассоциации) есть рецепт, позволяющий избежать таких ассоциаций с помощью "общей супер таблицы" (также называемой базовой таблицей или таблицей предков).
Полиморфно это будет:
table addresses:
id: integer
parent_type:string # 'A', 'B' or 'C'
parent_id: integer
Я знаю, что вы можете использовать таблицы пересечений, но следующее решение выглядит более отточенным:
Вместо полиморфного связывания A, B, C с Address, рецепт предлагает создать супер-таблицу (Addressing), которая имеет только поле id (суррогатный ключ или псевдоключ). Затем другие таблицы ссылаются на адресацию. Таким образом, говорит автор, "вы можете положиться на обеспечение целостности данных вашей базы данных с помощью внешних ключей". Итак, было бы:
table addressing
id: integer
table addresses
id: integer
addressing_id: integer (foreign_key)
zip: string
table a
id: integer
addressing_id: integer (foreign_key)
name: string
table b
id: integer
addressing_id: integer (foreign_key)
name: string
table c
id: integer
addressing_id: integer (foreign_key)
name: string
SQL-запрос будет выглядеть так:
SELECT * from a
JOIN address USING addressing_id
WHERE a.addressing_id = 1243
ВОПРОС: как кодировать такой сценарий в Rails? Я пытался несколькими способами без успеха.
1 ответ
Есть ли у объектов A, B и C один адрес? Или много адресов? Из вашего комментария ниже, похоже, что каждый объект имеет один адрес.
Если у каждого объекта есть только один адрес, вы можете просто вставить внешний ключ в объекты A/B/C с идентификатором адреса. Пусть объекты Дома или Офиса имеют один адрес:
class House < ActiveRecord::Base
belongs_to :address
end
class Office < ActiveRecord::Base
belongs_to :address
end
Ваш office
с и houses
Таблицы БД должны иметь внешний ключ address_id. Таким образом, вы можете получить доступ к адресу объекта с помощью чего-то вроде house.address
или же office.address
,
Если эти объекты могут иметь много адресов, решение зависит от объектов A/B/C. Если они связаны между собой, вы можете использовать Single Table Inheritance - Rails хорошо поддерживает этот шаблон, но без дополнительной информации сложно сказать, какой из них лучший.