0

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

每个帖子都应该只有一个特色图片,一个图片可以是多个帖子的特色图片,所以我的第一个想法就是颠倒关系,让图片has_many发布和发布belong_to图片,但这似乎有很多不同的问题. 首先,它不是很语义化。其次,post 控制器需要额外的代码来接受 image_id 的值,因为 Post.new 似乎不想接受 image_id 作为属性。

我的第二个想法——这是我目前要使用的一个——是在帖子和迁移中使用 HABTM 关系,并在limit: 1帖子has_and_belongs_to_many :featured_images和迁移中使用说明符。这个解决方案有效,但它似乎是 hack-ish。此外,这意味着我必须像这样访问特色图片,而不是. 更糟糕的是,我不禁认为这会损害数据库性能,因为它必须访问三个表而不是两个表,并且它必须在多对多表中搜索帖子 id,而不是通过 id 立即识别柱子。unique: truet.belongs_to :postpost.featured_images.firstpost.featured_image

那么,这是正确的方法还是有更好的方法?rails 有类似has_one,的belongs_to_many关系吗?

4

2 回答 2

3

why do not try something like that (without HABTM, just 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
于 2013-08-28T20:24:50.350 回答
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 而不触及其关联的帖子,那将无法节省您的时间。如果其他阅读本文的人对这个问题有一些见解,我很想听听。

于 2013-08-28T21:19:09.443 回答