问题描述
我有一个模型表示包含一些图像的 Content
项目.图像的数量是固定的,因为这些图像参考非常特定于内容.例如,Content
模型引用了 Image
模型两次(个人资料图片和背景图片).我试图避免使用通用的 has_many
,并坚持使用多个 has_one
.当前的数据库结构如下:
I have a model representing a Content
item that contains some images. The number of images are fixed as these image references are very specific to the content. For example, the Content
model refers to the Image
model twice (profile image, and background image). I am trying to avoid a generic has_many
, and sticking to multiple has_one
's. The current database structure looks like:
contents
- id:integer
- integer:profile_image_id
- integer:background_image_id
images
- integer:id
- string:filename
- integer:content_id
我就是不知道如何在此处正确设置关联.Content
模型可以包含对 Image
的两个 belongs_to
引用,但这在语义上似乎不正确,因为理想情况下图像属于内容,或者换句话说,内容有两张图片.
I just can't figure out how to setup the associations correctly here. The Content
model could contain two belongs_to
references to an Image
, but that doesn't seem semantically right cause ideally an image belongs to the content, or in other words, the content has two images.
这是我能想到的最好的(通过打破语义):
This is the best I could think of (by breaking the semantics):
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
我离题了,还有更好的方法来实现这种关联吗?
Am I way off, and there a better way to achieve this association?
推荐答案
简单的答案是设置与您现有的关联相反的关联,如下所示:
The simple answer is to setup your associations in reverse of what you have, like so:
# 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".
You don't need the foreign keys 'background_image_id' and 'profile_image_id' in the content table at all.
不过,还有一个更优雅的解决方案: 单表继承.如果您希望背景和个人资料图片在未来的行为略有不同,请立即进行设置,此外,它今天会阐明您的代码.
However, there's a more elegant solution: single table inheritance. Set it up now in case you want background and profile images to behave even slightly differently in the future, plus it will clarify your code today.
首先,在您的图像表中添加一个名为 type 的列:
First, add a column to your images table called type:
# command line
script/generate migration AddTypeToImages type:string
rake db:migrate
现在像这样设置你的模型:
Now setup your models like this:
# 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
现在您可以执行各种操作,例如获取所有背景图像的列表:
Now you can do all kinds of things like getting a list of all background images:
# script/console
BackgroundImage.all
这更符合您尝试创建的数据模型,允许将来最简单的可扩展性,并为您今天提供一些很酷的新方法.
This is more true to the data model you're trying to create, allows the easiest expandability in the future, and gives you some cool new methods today.
更新:
我已经创建了一篇名为 带测试的单表继承 更详细,涵盖了测试.
I've since created a blog article called Single-Table Inheritance with Tests that goes into more detail, and covers testing.
这篇关于Rails has_one 与belongs_to 语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!