如何在rails3中创建新记录之前检查记录是否存在?

inheritance人我正在努力实现的目标:

  • 我有一个标记系统。
  • 创建post时会创建标签(posthas_many:tags,:through =>:tag_joins。
  • 使用标记创建post时会自动创建标记连接。

我想检查标签是否已存在。 如果是的话,我想使用tag_join记录的现有标记,而不是创建新的标记记录。

这是我当前的代码,它不起作用。

class Tag  self.name, :user_id => current_user.id) if tag.nil? tag = Tag.create(:name => self.name, :user_id => current_user.id) end end end 

这不起作用,我在创建任务时遇到错误…(服务器实际上只是超时 – 我没有收到特定的错误)。

有任何想法吗?

Tokland说我通过告诉它再次创建标签来创建一个无限循环 – 所以我尝试了这个:

  def check_exists tag = Tag.find_by_name_and_user_id(:name => self.name, :user_id => current_user.id) if tag != nil self.id = tag.id end end 

并仍然得到服务器超时

编辑:我不确定这是否重要,但标签的添加方式类似于“http://railscasts.com/episodes/73-complex-forms-part-1

它们嵌套在post中,并使用这样的东西:

 def tag_attributes=(tag_attributes) tag_attributes.each do |attributes| tags.build(attributes) end end 

我想知道这是否会阻止整个工作? 另外,在模型中使用current_user.id肯定是一个问题……

编辑:

我已经想到的东西:这必须改变,我们以前使用的格式是不正确的语法 – 通常用于.where方法。

  def check_exists @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) if @tag != nil #return false #self=@tag end end 

现在的问题是,我可以了解标签是否已经存在。 但那又怎样? 如果我使用return false选项,则在创建post时会出现错误,并且不会创建连接记录…其他选项“self = @ tag”显然不起作用。

你会发现很难在Tag模型中找到它。 看起来你想要的是使用嵌套属性更新Post,如下所示:

 post = Post.create post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) 

使用虚拟属性setter方法实际上很简单:

 class Post < AR::Base has_many :tags def tags_attributes=(hash) hash.each do |sequence,tag_values| tags << Tag.find_or_create_by_name_and_user_id(tag_values[:name],\ tag_values[:user_id]) end end > post = Post.create > post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) > Tag.count # => 1 # updating again does not add dups > post.update_attributes(:tags_attributes=>{"0"=>{:name=>"fish",:user_id=>"37"}}) > Tag.count # => 1 

你知道在Rails中内置了一个find_by_or_create_by_函数,对吧?

 # No 'Summer' tag exists Tag.find_or_create_by_name("Summer") # equal to Tag.create(:name => "Summer") # Now the 'Summer' tag does exist Tag.find_or_create_by_name("Summer") # equal to Tag.find_by_name("Summer") 

http://api.rubyonrails.org/classes/ActiveRecord/Base.html (在基于动态属性的查找程序下)

你想使用魔术方法find_or_create_by

 def check_exists tag = Tag.find_or_create_by_name_and_user_id(:name => self.name, :user_id => current_user.id) end 

查看ActiveRecord :: Base文档以获取更多信息

我最初提出的问题在结尾时变得非常扭曲。 所以我把它分开了。

试图做我最初要求的人可以试试这个:

  before_create :check_tag exists private def check_tag_exists @tag = Tag.find_by_name_and_user_id(self.name, self.user_id) if @tag != nil # end end 

这将使您能够检查您的记录是否已创建。 任何进一步的逻辑你可以放入if if statment。

我相信其他答案有点过时了。 以下是你应该如何为Rails 4实现这一目标

 tag = Tag.first_or_initialize(:name => self.name, :user_id => current_user.id) if !tag.new_record? tag.id = self.id tag.save end 

试试这个

  def check_exists tag = Tag.where(:name => self.name, :user_id => current_user.id).first tag = Tag.new({:name => self.name, :user_id => current_user.id}) unless tag end 

使用Tag.new而不是Tag.create

在找不到匹配项时返回空的ActiveRecord。