Rails 4 - has_one 和 has_many 同时出现
Rails 4 - has_one and has_many at the same time
我有以下设置:
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" = 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
,然后在 class 上创建一个作用域关联,但我觉得这不是最佳选择:
- 我必须注意保持相册照片同步,所以只有一张布尔值设置为 1。
- 向照片 table 添加信息,将其与相册一相结合。如果我想要相册外的照片,它们会有两个额外的字段
is_profile_photo
和 is_cover_photo
,这没有意义。
是否有我缺少的 "Rails way" 来执行此操作?
我会在相册中添加 2 列 table
profile_photo_id
cover_photo_id
他们会持有作为个人资料 and/or 封面照片的照片的 ID。
然后,在相册模型中,您可以轻松添加:
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 => returns the photo with ID in the Album
Photo.profile_albums => returns all albums that have Photo as a profile picture
(封面同理)
我终于采用了我提到的第二种方法。
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:
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:
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
我有以下设置:
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" = 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
,然后在 class 上创建一个作用域关联,但我觉得这不是最佳选择:
- 我必须注意保持相册照片同步,所以只有一张布尔值设置为 1。
- 向照片 table 添加信息,将其与相册一相结合。如果我想要相册外的照片,它们会有两个额外的字段
is_profile_photo
和is_cover_photo
,这没有意义。
是否有我缺少的 "Rails way" 来执行此操作?
我会在相册中添加 2 列 table
profile_photo_id
cover_photo_id
他们会持有作为个人资料 and/or 封面照片的照片的 ID。
然后,在相册模型中,您可以轻松添加:
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 => returns the photo with ID in the Album
Photo.profile_albums => returns all albums that have Photo as a profile picture
(封面同理)
我终于采用了我提到的第二种方法。
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:
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:
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