Как реализовать наследование моделей или полиморфные ассоциации в Ruby on Rails?

STI хранит иерархию моделей в общей таблице, а полиморфная ассоциация связывает запись с объектами разных моделей. Это разные механизмы.

Короткий ответ

Что ответить на собеседовании

Для наследования моделей с общей таблицей используют STI: подклассы наследуют базовую модель, а столбец type по умолчанию хранит имя класса. Полиморфная ассоциация не является наследованием: belongs_to с polymorphic: true хранит пару идентификатор/тип связанного объекта. STI связывает модели общей схемой; полиморфная связь усложняет обеспечение ссылочной целостности обычными внешними ключами.

Подробный разбор

Ответ с пояснениями

Это две разные задачи. STI, или Single Table Inheritance, реализует наследование моделей с хранением в одной таблице. Полиморфная ассоциация описывает связь с объектами разных моделей, а не наследование между ними.

Для STI создадим общую таблицу. Фрагмент тела change в миграции:

create_table :animals do |t|
  t.string :type
  t.string :name
end

Модели размещаются в соответствующих файлах, например animal.rb, dog.rb и cat.rb:

class Animal < ApplicationRecord; end
class Dog < Animal; end
class Cat < Animal; end

dog = Dog.create!(name: 'Рекс')
Animal.find(dog.id).class # => Dog

По умолчанию Rails записывает имя подкласса в строковый столбец type и по нему восстанавливает объект. Отдельные таблицы dogs и cats здесь не нужны. Столбец можно переименовать настройкой inheritance_column; нельзя бездумно использовать type для постороннего бизнес-поля. Настройка inheritance_column.

Для комментариев к статьям и фотографиям наследование не требуется. Предположим, таблицы articles и photos уже существуют. Фрагмент другой миграции:

create_table :comments do |t|
  t.text :body
  t.references :commentable, polymorphic: true, null: false, index: true
end

Соответствующие модели:

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

class Article < ApplicationRecord
  has_many :comments, as: :commentable
end

class Photo < ApplicationRecord
  has_many :comments, as: :commentable
end

Поля commentable_type и commentable_id определяют тип и идентификатор владельца. comment.commentable вернёт соответствующую статью или фотографию. При переименовании модели нужно согласовать сохранённые значения типа с кодом.

STI удобно при близких атрибутах и поведении, но общая схема может обрастать столбцами, нужными лишь одному подклассу. Полиморфная связь гибче по составу владельцев, однако обычный внешний ключ не может по значению типа выбирать одну из нескольких целевых таблиц. Правила удаления и ссылочную целостность приходится проектировать отдельно; null: false проверяет лишь отсутствие NULL. Руководство по ассоциациям и STI.

Практика в реальном времени

Подготовьтесь к следующему собеседованию

Interview Boost учитывает вакансию, резюме и технологии и помогает сформулировать ответ прямо во время интервью.

Начать подготовку