2012-12-04 73 views
2

我有兩個模型,歌曲和顯示。演出是歌曲的有序列表,其中同一首歌曲可以多次列出。如何讓一個有序的模型關聯允許重複

也就是說,在Show中應該有一個有序數組(或者是散列或者其他),它可以包含Song1,Song2,Song1,Song3,並允許從該數組重新排序,插入或刪除。

我無法弄清楚如何使用ActiveRecord關聯進行建模。我猜我需要某種特殊的連接表和索引列,但除了開始直接編寫我的SQL之外,有沒有辦法通過Rails關聯來實現?

一些代碼,因爲我有現在的(但不正常):

class Song < ActiveRecord::Base 
    attr_accessible :title 
    has_and_belongs_to_many :shows 
end 

class Show < ActiveRecord::Base 
    attr_accessible :date 
    has_and_belongs_to_many :songs 
end 

song1 = Song.create(title: 'Foo') 
song2 = Song.create(title: 'Bar') 
show1 = Show.create(date: 'Tomorrow') 

show1.songs << song1 << song2 << song1 

puts "show1 size = #{show1.songs.size}" # 3 
show1.delete_at(0) # Should delete the first instance of song1, but leave the second instance 
puts "show1 size = #{show1.songs.size}" # 2 
show1.reload 
puts "show1 size = #{show1.songs.size}" # 3 again, annoyingly 

插入可能看起來像:

show1.songs # Foo, Bar, Foo 
song3 = Song.create(title: 'Baz') 
show1.insert(1, song3) 
show1.songs # Foo, Baz, Bar, Foo 

和重排序可能(用小魔術)看起來例如:

show1.songs # Foo, Bar, Foo 
show1.move_song_from(0, to: 1) 
show1.songs # Bar, Foo, Foo 

回答

0

我目前的解決辦法是的has_many的組合:通過和acts_as_list。找到正確結合兩者的信息並不是最容易的事情。例如,其中一個障礙是acts_as_list使用從1開始的索引,而由ActiveRecord關聯創建的類似數組的方法從0開始。

以下是我的代碼如何結束。請注意,我必須指定顯式方法來修改連接表(無論如何,大部分都是這樣);我不確定是否有更清晰的方式來完成這些工作。

class Song < ActiveRecord::Base 
    attr_accessible :title 
    has_many :playlist_items, :order => :position 
    has_many :shows, :through => :playlist_items 
end 

class PlaylistItem < ActiveRecord::Base 
    attr_accessible :position, :show_id, :song_id 
    belongs_to :shows 
    belongs_to :songs 
    acts_as_list :scope => :show 
end 

class Show < ActiveRecord::Base 
    attr_accessible :date 
    has_many :playlist_items, :order => :position 
    has_many :songs, :through => :playlist_items, :order => :position 

    def song_at(index) 
    self.songs.find_by_id(self.playlist_items[index].song_id) 
    end 

    def move_song(index, options={}) 
    raise "A :to option is required." unless options.has_key? :to 
    self.playlist_items[index].insert_at(options[:to] + 1) # Compensate for acts_as_list starting at 1 
    end 

    def add_song(location) 
    self.songs << location 
    end 

    def remove_song_at(index) 
    self.playlist_items.delete(self.playlist_items[index]) 
    end 
end 

根據acts_as_list附帶的說明,我在'playlist_items'表中添加了'position'列。值得注意的是,我必須深入到acts_as_list的API才能找到insert_at方法。

0

您在正確的軌道上連接表想法:

class Song < ActiveRecord::Base 
    attr_accessible :title 
    has_many :playlist_items 
    has_many :shows, :through => :playlist_items 
end 

class PlaylistItem < ActiveRecord::Base 
    belongs_to :shows #foreign_key show_id 
    belongs_to :songs #foreign_key song_id 
end 

class Show < ActiveRecord::Base 
    attr_accessible :date 
    has_many :playlist_items 
    has_many :songs, :through => :playlist_items 
end 

然後,你可以做的東西一樣user.playlist_items.create :song => Song.last

+0

我得到了一個has_many:通過是票,但這似乎仍然很像habtm。我如何刪除Show中特定索引處的歌曲?那就是像show.playlist_items.delete_at(4)? – sirbrillig

+0

是的,您將如何刪除它們。你是對的,他們是相似的(都使用連接表),但我更喜歡使用'has_many =>:through',因爲它可以讓你直接訪問連接表(如果你需要添加驗證/額外的屬性等)。 – David

+0

說到這一點,你是否爲上面的habtm創建了'shows_songs(show_id:integer,song_id:integer)'連接表? – David

相關問題