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

时间:2011-02-12 15:36:26

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

这是我想要完成的事情:

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

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

这是我当前的代码,但无效。

class Tag < ActiveRecord :: Base
  belongs_to :user
  belongs_to :tag_join
  belongs_to :post

  before_create :check_exists

  def check_exists
    tag = Tag.where(:name => 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

它们嵌套在帖子中,并使用类似的东西:

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选项,则在创建帖子时会出现错误,并且不会创建连接记录...其他选项“self = @ tag”显然不起作用。

7 个答案:

答案 0 :(得分:12)

您将很难从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

答案 1 :(得分:9)

Rails中内置了一个find_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(在基于动态属性的查找器下)

答案 2 :(得分:6)

您想要使用魔术方法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文档以获取更多信息

答案 3 :(得分:4)

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

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

 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

这将使您能够检查您的记录是否已创建。如果是法定的话,你可以放下任何进一步的逻辑。

答案 4 :(得分:3)

我相信其他答案有点陈旧。以下是你应该如何为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

答案 5 :(得分:0)

试试这个

  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

答案 6 :(得分:-2)

其中返回空的ActiveRecord,找不到匹配项。