2012-03-30 49 views
4

私はhas_many throughアソシエーション設定をソングモデルとアーティストモデルの間に持っています。 私のコードは、このRailsは重複を避けるためにhas_many

SongArtistMapモデルのようなもの

class SongArtistMap < ActiveRecord::Base 
belongs_to :song 
belongs_to :artist 
end 

アーティストモデル

class Artist < ActiveRecord::Base 
has_many :song_artist_maps 
has_many :songs, :through => :song_artist_maps 

validates_presence_of :name 
end 

ソングモデル

class Song < ActiveRecord::Base 
    has_many :song_artist_maps 
    has_many :artists, :through => :song_artist_maps 
    accepts_nested_attributes_for :artists 
end 

に見える私は、ユーザーが歌を提出して入る形を持っています曲のタイトルと曲のアーティストで。

したがって、ユーザーは歌を提出し、ユーザーがで歌を提出した場合、私のアーティストテーブルにはすでに、私はそれがそのアーティストとセットアップ

SongArtistMap

でマップを作成したい曲のアーティストを持っていないときArtistテーブルに既に入っているアーティスト私は、SongArtistMapを作成したいがアーティストは複製されていないようにしたい。

現在、ユーザーが曲をサブミットするたびに、同じアーティストが既に存在していてもそのアーティストのテーブルに新しいアーティストが作成され、そのコピーされたアーティストのSongArtistMapが作成されます。

どのようにこの問題に取り組んでいますか?レールがおそらくすでに組み込まれているこの問題を解決する簡単な小さなトリックを持っているような気がする。ありがとう!

+2

あなたはメソッドを見つけることを知っていますか?あなたはメソッドの作成を知っていますか?まあ、Railsにはfind_or_create_by_attributeというメソッドがあります!したがって、あなたの場合、find_or_create_by_nameを使用することができます。ただし、ネストされた属性を使用しているので... [検索または作成でネストされた属性を受け入れる](http://stackoverflow.com/questions/3579924/accepts-nested-attributes-for-with-find-or-create) 。そう、ええ、これは重複した質問です。 – Ashitaka

答えて

1

[OK]を実行して、これを最適化することができます。だからここに私の問題を解決した。まず第一に、私はhas_many throughの関係を持つ必要がないことに気付きました。

本当に必要なのはhas_and_belongs_to_manyの関係でした。セットアップしてテーブルを作った。

は、その後、私はこの

def self.find_or_create_by_name(name) 
    k = self.find_by_name(name) 

    if k.nil? 
    k = self.new(:name => name) 
    end 

    return k 
end 

そして、私のSongモデルに追加された私のArtistsモデルで私はこの

before_save :get_artists 
def get_artists 
    self.artists.map! do |artist| 
    Artist.find_or_create_by_name(artist.name) 
    end 
end 

を追加しましたそして、それは私が望んでいたまさにでした。

0

私はテーブルのモデルで、他の2つが通過するbefore_createで呼び出されるメソッドを使用します。これは、おそらくもっときめ細かく、より速く作ることができます。

before_create :ensure_only_one_instance_of_a_user_in_a_group 

    private 

    def ensure_only_one_instance_of_a_user_in_a_group 
    user = User.find_by_id(self.user_id) 
    unless user.groups.empty? 
     user.groups.each do |g| 
     if g.id == self.group_id 
      return false 
     end 
     end 
    end 
    return true 
    end 
0

これを試してみてください:

class Song < ActiveRecord::Base 
    has_many :song_artist_maps 
    has_many :artists, :through => :song_artist_maps 
    accepts_nested_attributes_for :artists, :reject_if => :normalize_artist 


    def normalize_artist(artist) 
    return true if artist['name'].blank? 
    artist['id'] = Artist.find_or_create_by_name(artist['name']).id 
    false # This is needed 
    end 
end 

我々は、本質的に、オーバーロードreject_if機能を(我々はtrueを返すことはありませんよう)によってレールをだましています。

あなたは、さらに私は、これはしばらく前に考え出したと投稿するのを忘れてしまった(あなたがMySQLの上にある場合は必須ではありません)大文字小文字を区別しない検索を

artist['id'] = ( 
    Artist.where("LOWER(name) = ? ", artist['name'].downcase).first ||  
    Artist.create(:name => artist['name']) 
    ).id 
+0

これを試してみましたが、それは効果的ではないようですが、私はまだ重複したアーティストを作成します。 – Dan

+0

falseを返す前に 'p artist'を追加してデバッグします。 idが正しく設定されているかどうかを確認してください。 –

+0

私はfalseを返す前にpアーティストを追加しますが、何も得られません。そのリターンは、画面上またはログのどこかに表示されるはずですか?また、ここに私がhttp://pastebin.com/AfwxRackフォームを提出するとサーバーログがあります – Dan

関連する問題