2012-03-30 50 views
4

我有一个has_many通过歌曲模型和艺术家模型之间的关联设置。 我的代码看起来是这样的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

歌曲,艺术家如果用户提交的歌曲有已经在Artists表中的艺术家我只想创建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 2012-03-30 01:39:09

回答

1

好的优化这个我这一段时间前想通了,忘了张贴。所以这是我如何解决我的问题。首先我意识到我不需要有一个has_many through的关系。

我真正需要的是一个has_and_belongs_to_many的关系。我设置了它并为它创建了表格。

然后在我Artists模型添加此

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模型添加此

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

而这也正是我想要的。

0

我在表格模型中使用另外两种方法,即用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 2012-03-30 04:47:59

+0

在返回false前加入'p artist'进行调试。查看该ID是否设置正确。 – 2012-03-30 04:57:17

+0

在返回false之前添加p艺术家,但我什么都没有。该回报是否应该显示在屏幕上或某处的日志中?另外这里的服务器日志,当我提交表单http://pastebin.com/AfwxRack – Dan 2012-03-31 20:23:57