Как избежать полиморфных ассоциаций в Rails

(извините за плохой английский)

Предположим, у меня есть модели 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? Я пробовал несколькими способами, но безуспешно.


person Fernando Fabreti    schedule 09.03.2012    source источник
comment
Вы можете посмотреть руководство RoR об ассоциациях [1], возможно, вы могли бы найти способ сделать это с помощью опции: through, извините, но я не могу попробовать сейчас. [1]: guides.rubyonrails.org/   -  person Aldo 'xoen' Giambelluca    schedule 11.03.2012


Ответы (1)


У объектов A, B и C один адрес? Или много адресов? Судя по вашему комментарию ниже, у каждого объекта один адрес.

Если у каждого объекта есть только один адрес, вы можете просто поместить внешний ключ в объекты A / B / C с идентификатором адреса. Пусть у объектов Дома или Офиса будет один адрес:

class House < ActiveRecord::Base
  belongs_to :address
end

class Office < ActiveRecord::Base
  belongs_to :address
end

Ваши offices и houses таблицы БД должны иметь address_id внешнего ключа. Таким образом, вы можете получить доступ к адресу объекта с помощью чего-то вроде house.address или office.address.

Если у этих объектов может быть много адресов, решение зависит от объектов A / B / C. Если они связаны, вы можете использовать наследование одной таблицы - Rails поддерживает этот шаблон хорошо - но, не имея дополнительной информации, трудно сказать, какой подход является лучшим.

person Aldo 'xoen' Giambelluca    schedule 10.03.2012
comment
2-й. Линия вопроса: У каждой модели один адрес. Не могли бы вы опубликовать код, чтобы я мог его протестировать? Похоже, ваше решение требует foreign_keys для A / B / C в таблицах адресов, отражающих ассоциацию has_one, что может вызвать много нулевых значений, поскольку адрес связан исключительно с A, B или C - person Fernando Fabreti; 10.03.2012
comment
Каждая модель имеет один адрес, поэтому вы помещаете ссылку на этот адрес в свои модели A / B / C, у вас не так много значений nil, поскольку каждый объект A имеет или не имеет адреса. Я обновлю свой ответ, чтобы лучше объяснить. - person Aldo 'xoen' Giambelluca; 10.03.2012
comment
Выглядит неплохо! А как насчет ссылочной целостности? Разумеется, own_to: address,: independent = ›: destroy для случаев, когда вы уничтожаете домашний объект. Но как насчет уничтожения адреса? - person Fernando Fabreti; 11.03.2012
comment
@FernandoFabreti ммм, сложно связать адрес с чем-то без ссылки, вы можете добавить after_destroy в модель Address и искать объекты с address_id, равным идентификатору уничтоженного адреса, но вы должны искать в таблице офисов и домов, а это не решение такое красивое. - person Aldo 'xoen' Giambelluca; 11.03.2012