Rails has_one vs belongs_to语义

时间:2010-01-25 22:18:27

标签: ruby-on-rails model associations

我有一个代表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 个答案:

答案 0 :(得分:23)

简单的答案是以你所拥有的相反方式设置你的联想,如下所示:

# 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

对于您尝试创建的数据模型更为正确,允许将来最简单的可扩展性,并为您提供一些很酷的新方法。

<强>更新

我之后创建了一篇名为Single-Table Inheritance with Tests的博客文章,该文章详细介绍了测试。

答案 1 :(得分:1)

基于the AR associations guide,我认为您应该使用has_one。拥有内容的图像没有意义......内容肯定拥有图像。从指南:

  

区别在于您放置的位置   外键(它放在桌子上   为声明belongs_to的类   协会),但你应该给一些   想到的实际意义   数据也是如此。 has_one关系   说有一件事是你的 -   也就是说,有些事情可以追溯到   你。

最后,我不确定您是否需要内容和图像才能拥有外键。只要图像引用content_id,我认为你没事。