Problems modeling the concept of "featured artists" (on rap songs)
Sometimes rap songs have more than one artist. For example, Nicki Minaj's "Roman's Revenge" features Eminem,开发者_Go百科 and thus appears as "Nicki Minaj (Ft. Eminem) – Roman's Revenge" in the Rap Genius catelog.
On Rap Genius I model featured artists via a performances
join model that has the following attributes:
song_id
artist_id
role
(either "primary" or "featured")
So:
In
artist.rb
:has_many :songs, :through => :performances
In
song.rb
:has_many :artists, :through => :performances
In song.rb
:
def primary_artists
performances.select{|p| p.role == 'primary'}.map(&:artist).compact
end
def featured_artists
performances.select{|p| p.role == 'featured'}.map(&:artist).compact
end
# from the user's perspective there's only one primary artist
def primary_artist
primary_artists.first
end
The question is how to implement Song#primary_artist=
and Song#featured_artists=
. Right now I'm doing this, which is buggy:
def primary_artist=(artist)
return if artist.blank?
Performance.song_id_eq(id).role_eq('primary').destroy_all
performances.build(:artist => artist, :role => 'primary')
end
The reason this is buggy is that this method destroys all existing primary artists on the spot, but the replacement primary artist is only created when the song is saved. So, if the song save fails, it's primary artist will be deleted.
What's the right way to do this? We want the old primary artist to be removed only if the song save is successful, so one idea is:
def primary_artist=(artist)
return if artist.blank?
#Performance.song_id_eq(id).role_eq('primary').destroy_all
@performances_to_destroy << Performance.song_id_eq(id).role_eq('primary')
performances.build(:artist => artist, :role => 'primary')
end
def after_save
@performances_to_destroy.each(&:destroy)
end
But this still seems a bit confusing / hackish.
A slightly different idea, but wouldn't it be a bit simpler and clearer to have two relationships between artists and songs here, i.e. a song would have relationships to artists along the lines of:
belongs_to :primary_artist, :class_name => "Artist"
has_and_belongs_to_many :featured_artists, :class_name => "Artist"
精彩评论