2

Song と Show の 2 つのモデルがあります。ショーは曲の順序付きリストであり、同じ曲を複数回リストすることができます。

つまり、Song1、Song2、Song1、Song3 を含む順序付き配列 (またはハッシュなど) が Show のどこかに存在し、その配列から並べ替え、挿入、または削除ができる必要があります。

これを 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

これに対する私の現在の解決策は、has_many :through と act_as_list の組み合わせです。この 2 つを正しく組み合わせる方法に関する情報を見つけるのは、簡単なことではありませんでした。たとえば、障害の 1 つは、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

act_as_list に付属の説明書に従って、'playlist_items' テーブルに 'position' 列を追加しました。insert_at メソッドを見つけるために、acts_as_list の API を掘り下げる必要があったことは注目に値します。

于 2012-12-06T05:03:30.773 に答える
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 に答える