Reputation: 5367
I have the following setup:
class Album < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :album
end
What I would like to do, is defining a profile_photo
and a cover_photo
for each album, but I'm not sure on how to approach the design.
First attempt has been to use has_one :profile_photo, class: 'Photo'
with a migration creating a albums.profile_photo_id
field and a has_one :album
on the Photo
model. Didn't work, as the SQL query generated was not correct:
> 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">
Another approach would be to add booleans to the Photo
model, like photos.is_profile_photo
and then on the class create a scoped association, but I feel this is not optimal as:
is_profile_photo
and is_cover_photo
that would not make sense.Is there a "Rails way" to do this that I'm missing?
Upvotes: 0
Views: 604
Reputation: 5367
I have finally gone with the second approach I mentioned.
class AddCoverAndProfileToPhotos < ActiveRecord::Migration
def change
add_column :photos, :is_profile, :boolean, default: false
add_column :photos, :is_cover, :boolean, default: false
end
end
Album 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
And finally 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
Upvotes: 0
Reputation: 56
I would add 2 columns to the Album table
profile_photo_id
cover_photo_id
They would hold the id of the photo that was the profile and/or cover photo.
Then, in the Album model, you can easily add:
belongs_to :profile_photo, class_name: "Photo"
belongs_to :cover_photo, class_name: "Photo"
Then in the Photo model, you need:
has_many :profile_albums, class_name: "Album", foreign_key: "profile_photo_id"
has_many :cover_albums, class_name: "Album", foreign_key: "cover_photo_id"
(Note you can name them whatever you want, I picked these. You just need the class_name and foreign_key to point to the correct model with that id column)
Then you have the following associations:
Album.profile_photo => returns the photo with ID in the Album
Photo.profile_albums => returns all albums that have Photo as a profile picture
(the same applies to cover photo)
Upvotes: 2