【问题标题】:Rails 4 - has_one and has_many at the same timeRails 4 - has_one 和 has_many 同时
【发布时间】:2015-05-21 21:43:16
【问题描述】:

我有以下设置:

class Album < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :album
end

我想做的是为每张专辑定义一个profile_photo 和一个cover_photo,但我不确定如何进行设计。

第一次尝试是使用has_one :profile_photo, class: 'Photo' 进行迁移,在Photo 模型上创建albums.profile_photo_id 字段和has_one :album。不起作用,因为生成的 SQL 查询不正确:

> album.profile_photo

Photo Load (0.4ms)  SELECT  "photos".* FROM "photos" WHERE "photos"."album_id" = $1 LIMIT 1  [["album_id", 16]]

=> #<Photo id: 14, album_id: 16, image: "image_l.jpg", created_at: "2015-05-21 20:03:42", updated_at: "2015-05-21 20:03:42">

另一种方法是向Photo 模型添加布尔值,例如photos.is_profile_photo,然后在类上创建一个作用域关联,但我觉得这不是最佳选择:

  • 我必须注意保持相册照片同步,所以只有一张布尔值设置为 1。
  • 将信息添加到照片表,将其与相册一相结合。如果我想在相册之外添加照片,它们将有两个额外的字段 is_profile_photois_cover_photo,这是没有意义的。

有没有我想念的“Rails 方式”来做到这一点?

【问题讨论】:

  • 我不确定我是否完全理解您的用例,但您是否看过 STI?如果您不熟悉,here 是一个很好的概述。
  • 我不认为这是一个复杂的用例,只是尝试使用一堆照片的相册模型,其中两张需要标记为特殊(个人资料和封面),Facebook 风格。不确定 STI 如何适合这里,感觉为 ProfilePhoto / CoverPhoto 提供不同的课程感觉太过分了(如果这是你的建议)。

标签: ruby-on-rails-4 associations


【解决方案1】:

我会在相册表中添加 2 列

profile_photo_id
cover_photo_id

他们将持有作为个人资料和/或封面照片的照片的 ID。

然后,在 Album 模型中,您可以轻松添加:

belongs_to :profile_photo, class_name: "Photo"
belongs_to :cover_photo, class_name: "Photo"

那么在照片模型中,你需要:

has_many :profile_albums, class_name: "Album", foreign_key: "profile_photo_id"
has_many :cover_albums, class_name: "Album", foreign_key: "cover_photo_id"

(请注意,您可以随意命名它们,我选择了这些。您只需要 class_name 和 foreign_key 指向具有该 id 列的正确模型)

那么你有以下关联:

Album.profile_photo => 返回相册中带有 ID 的照片

Photo.profile_albums => 返回所有以照片为头像的相册

(封面照也是如此)

【讨论】:

  • 这是我尝试的第一件事,如问题中所述。问题是has_one 期望在关系的另一侧有一个belongs_to,并且在执行@album.profile_photo 时生成的SQL 查询会检查photos.album_id 而不是您提到的两列。
  • 我修好了。所以关联是倒退的,相册中有一个belongs_to,照片中有has_many。我已经用我的快速轨道应用程序对其进行了测试,但如果它不起作用/没有意义,请告诉我。
【解决方案2】:

我终于采用了我提到的第二种方法。

class AddCoverAndProfileToPhotos < ActiveRecord::Migration
  def change
    add_column :photos, :is_profile, :boolean, default: false
    add_column :photos, :is_cover, :boolean, default: false
  end
end

专辑类:

class Album < ActiveRecord::Base   
  has_many :photos, inverse_of: :album, dependent: :destroy

  has_one :profile_photo,
    -> { where is_profile: true },
    class_name: 'Photo'

  has_one :cover_photo,
    -> { where is_cover: true },
    class_name: 'Photo'

  accepts_nested_attributes_for :photos, allow_destroy: true

  delegate :empty?, to: :photos

  validates_associated :photos

  def remove_profile_photo
    profile_photo.update(is_profile: false) if profile_photo.present?
  end

  def remove_cover_photo
    cover_photo.update(is_cover: false) if cover_photo.present?
  end

  def set_profile_photo(photo)
    remove_profile_photo
    photo.update(is_profile: true)
  end

  def set_cover_photo(photo)
    remove_cover_photo
    photo.update(is_cover: true)
  end
end

最后是照片类:

class Photo < ActiveRecord::Base
  mount_uploader :image, PhotoUploader

  belongs_to :album
  validates_presence_of :image
  validates_presence_of :album

  validates_uniqueness_of :is_profile, scope: :album, if: :is_profile?
  validates_uniqueness_of :is_cover, scope: :album, if: :is_cover?
end

【讨论】:

    猜你喜欢
    • 2018-04-08
    • 1970-01-01
    • 2019-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多