我在歌曲模型和艺术家模型之间设置了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可能有一些简单的小技巧来修复已经内置的东西。谢谢!
答案 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