Rails 4 - has_one和has_many同时

时间:2015-05-21 21:43:17

标签: ruby-on-rails-4 associations

我有以下设置:

class Album < ActiveRecord::Base
  has_many :photos
end

class Photo < ActiveRecord::Base
  belongs_to :album
end

我想做的是为每张专辑定义profile_photocover_photo,但我不确定如何接近设计。

首次尝试使用has_one :profile_photo, class: 'Photo'进行迁移,在albums.profile_photo_id模型上创建has_one :album字段和Photo。没有工作,因为生成的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。
  • 将信息添加到将其与专辑1相关联的照片表中。如果我想在相册外面放置照片,则会有两个额外的字段is_profile_photois_cover_photo,这些字段没有意义。

是否有&#34; Rails方式&#34;这样做我错过了吗?

2 个答案:

答案 0 :(得分:2)

我会在Album表中添加2列

profile_photo_id
cover_photo_id

他们会保留照片的个人资料和/或封面照片。

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

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

然后在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 =&gt;返回相册中带有ID的照片

     

Photo.profile_albums =&gt;返回将照片作为个人资料图片的所有相册

(同样适用于封面照片)

答案 1 :(得分:0)

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

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

最后是Photo class:

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