Rails has_one vs принадлежит к семантике
У меня есть модель, представляющая элемент Content
, который содержит некоторые изображения. Количество изображений фиксировано, так как эти ссылки на изображения очень специфичны для содержимого. Например, модель Content
относится к модели Image
дважды (изображение профиля и фоновое изображение). Я пытаюсь избежать общего has_many
и придерживаться нескольких has_one
. Текущая структура базы данных выглядит так:
contents
- id:integer
- integer:profile_image_id
- integer:background_image_id
images
- integer:id
- string:filename
- integer:content_id
Я просто не могу понять, как правильно настроить ассоциации здесь. Модель Content
может содержать две ссылки belongs_to
для Image
, но это не кажется семантически правильным, потому что в идеале изображение принадлежит содержимому, или, другими словами, контент имеет два изображения.
Это лучшее, что я мог подумать (сломав семантику):
class Content
belongs_to :profile_image, :class_name => 'Image', :foreign_key => 'profile_image_id'
belongs_to :background_image, :class_name => 'Image', :foreign_key => 'background_image_id'
end
У меня нет выхода, и есть ли лучший способ достичь этой ассоциации?
Ответы
Ответ 1
Простой ответ заключается в настройке ваших ассоциаций в обратном порядке, как у вас:
# app/models/content.rb
class Content < ActiveRecord::Base
has_one :profile_image, :class_name => 'Image'
has_one :background_image, :class_name => 'Image'
end
# app/models/image.rb
class Image < ActiveRecord::Base
belongs_to :content
end
Вам вообще не нужны внешние символы 'background_image_id' и 'profile_image_id' в таблице содержимого.
Однако существует более элегантное решение: однонаправленное наследование таблицы. Настройте его сейчас, если вы хотите, чтобы в будущем фоновые изображения и изображения профилей несколько отличались друг от друга, а также теперь прояснит ваш код.
Сначала добавьте столбец в таблицу изображений, называемую type:
# command line
script/generate migration AddTypeToImages type:string
rake db:migrate
Теперь настройте свои модели следующим образом:
# app/models/content.rb
class Content < ActiveRecord::Base
has_one :profile_image
has_one :background_image
end
# app/models/image.rb
class Image < ActiveRecord::Base
belongs_to :content
end
# app/models/background_image.rb
class BackgroundImage < Image
# background image specific code here
end
# app/models/profile_image.rb
class ProfileImage < Image
# profile image specific code here
end
Теперь вы можете делать всевозможные вещи, например, получать список всех фоновых изображений:
# script/console
BackgroundImage.all
Это более верно для модели данных, которую вы пытаетесь создать, позволяет в будущем легче всего расширять возможности и предлагает вам несколько новых современных методов.
UPDATE:
С тех пор я создал статью в блоге под названием "Наследование на основе одиночных таблиц с тестами" , которая идет более подробно и охватывает тестирование.
Ответ 2
На основе руководства ассоциаций AR, я думаю, вы должны использовать has_one
. Для изображения не имеет смысла владеть контентом... контент, безусловно, принадлежит изображению. Из руководства:
Различие заключается в том, где вы размещаете внешний ключ (он идет на стол для класса, объявляющего свойство belongs_to ассоциация), но вы должны дать некоторые думал о реальном значении данных. Отношение has_one говорит, что одно из вас - ваше - то есть что-то указывает на вы.
Наконец, я не уверен, что вам нужно как содержимое, так и изображения иметь внешние ключи. Пока изображения ссылаются на content_id, я думаю, что все в порядке.