2

我有两个模型,Song 和 Show。一个节目是一个有序的歌曲列表,其中同一首歌曲可以被多次列出。

也就是说,在 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
4

2 回答 2

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

于 2012-12-04T23:27:47.340 回答
0

我目前对此的解决方案是 has_many :through 和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”表中添加了一个“位置”列。值得注意的是,我必须深入研究acts_as_list 的API 才能找到insert_at 方法。

于 2012-12-06T05:03:30.773 回答