做has_one和belongs_to_many的最佳方法是什么?

时间:2013-08-28 18:16:24

标签: sql ruby-on-rails database activerecord activemodel

我有一个类似于博客的rails项目,其帖子包含一组图像和一个特色图像。图像集是一个非常直接的HABTM关系,因为几个帖子可以共享相同的图像,一个帖子可以有很多图像,但特色图像有点麻烦。

每个帖子都应该有一个且只有一个特色图片,一个图片可以是几个帖子上的精选图片,所以我的第一个想法就是扭转关系,让图片has_many发帖和帖子{{1}图像,但这在很多不同的方面似乎有问题。首先,它不是很语义。其次,post控制器需要额外的代码来接受image_id的值,因为Post.new似乎不想接受image_id作为属性。

我的第二个想法 - 这是我到目前为止的一个想法 - 是在帖子的belong_tolimit: 1上使用has_and_belongs_to_many :featured_images说明符来使用HABTM关系迁移中的unique: true上的{1}}这个解决方案有效,但看起来很糟糕。此外,这意味着我必须访问此t.belongs_to :post而不是post.featured_images.first的精选图片。更糟糕的是,我不得不认为这会损害数据库性能,因为它必须访问三个表而不是两个表,并且它必须在多对多表中搜索post id,而不是通过id进行即时识别列。

那么,这是正确的做法还是有更好的方法? rails是否有post.featured_imagehas_one关系?

2 个答案:

答案 0 :(得分:3)

为什么不尝试类似的东西(没有HABTM,只有has_many):

class Image < ActiveRecord::Base
  belongs_to :post
  attr_accessible :featured

  after_commit :reset_featured, if: :persisted?

  protected
  # garant that featured will be only one
  def reset_featured
    Image.where('id <> ?', self.id).update_all(featured: false) if self.featured
  end
end

class Post < ActiveRecord::Base
  has_many :images, conditions: { featured: false }
  has_one :featured_image, class_name: 'Image', conditions: { featured: true }
end

答案 1 :(得分:1)

由于这是一个“拥有并且属于许多”关系的情况,但是你想要存储关于关系本身的额外信息(事实上图像是“特色”的帖子),我会尝试而是has_many :through安排。像这样:

class Post < ActiveRecord::Base
  has_many :post_images, inverse_of: :post
  has_many :images, through: :post_images
  has_one :featured_post_image, class_name: PostImage,
    inverse_of: :post, conditions: { is_featured: true }
  has_one :featured_image, through: :featured_post_image

  accepts_nested_attributes_for :post_images, allow_destroy: true
  attr_accessible :post_images_attributes
end

class PostImage < ActiveRecord::Base
  belongs_to :post
  belongs_to :image

  attr_accessible :image_id
end

class Image < ActiveRecord::Base
  has_many :post_images
  has_many :posts, through: :post_images
end

不幸的是,添加验证以确保帖子永远不会有多个精选图片比看起来更棘手。您可以对Post进行验证,但如果您的应用的其他部分直接创建PostImages而不触及相关帖子,则无法保存。如果有人读到这篇文章对这个问题有一些了解,我很乐意听到它。