Rails has_one противостоит семантике - PullRequest
18 голосов
/ 26 января 2010

У меня есть модель, представляющая элемент 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

Я далеко, и есть ли лучший способ для достижения этой ассоциации?

Ответы [ 2 ]

23 голосов
/ 26 января 2010

Простой ответ - настроить ваши ассоциации в противоположность тому, что у вас есть, например:

# 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:

С тех пор я создал статью для блога под названием Наследование в одной таблице с тестами , в которой более подробно описывается тестирование.

1 голос
/ 26 января 2010

Основываясь на Руководстве по связям AR , я думаю, вы должны использовать has_one Для изображения не имеет смысла владеть Контентом ... Контенту, несомненно, принадлежит изображение. Из путеводителя:

Различие в том, где вы размещаете внешний ключ (он идет на стол для класса, объявляющего принадлежащего ассоциация), но вы должны дать мысль к реальному значению данные также. Отношение has_one говорит, что одно из твоего - то есть что-то указывает на вы.

Наконец, я не уверен, что вам нужны и контент, и изображения, чтобы иметь внешние ключи. Пока изображения ссылаются на content_id, я думаю, что вы в порядке.

...