开发者

Problems modeling the concept of "featured artists" (on rap songs)

开发者 https://www.devze.com 2023-01-23 23:02 出处:网络
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 th

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"
0

精彩评论

暂无评论...
验证码 换一张
取 消