如何使有序的模型关联允许重复

时间:2012-12-04 23:15:51

标签: ruby activerecord model associations

我有两个型号,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

2 个答案:

答案 0 :(得分: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

之类的操作

答案 1 :(得分: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

我添加了一个位置&#39;列到我的播放列表项目&#39;表,根据acts_as_list附带的说明。值得注意的是,我必须深入了解acts_as_list的API以找到insert_at方法。