Rails可以避免重复

时间:2012-03-30 01:25:42

标签: ruby-on-rails ruby-on-rails-3

我在歌曲模型和艺术家模型之间设置了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。

有关如何解决此问题的任何想法?我觉得rails可能有一些简单的小技巧来修复已经内置的东西。谢谢!

3 个答案:

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

这正是我想要的。

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

答案 2 :(得分: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函数来欺骗rails(因为我们永远不会返回true)。

您可以通过不区分大小写的查找来进一步优化这一点(如果您使用MySQL,则不需要)

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