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

Ell*_*iot 5 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
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

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
Run Code Online (Sandbox Code Playgroud)

仍然得到服务器超时

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

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

def tag_attributes=(tag_attributes)
  tag_attributes.each do |attributes|
    tags.build(attributes)
  end
end
Run Code Online (Sandbox Code Playgroud)

我想知道这是否会阻止整个工作?此外,在模型中使用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
Run Code Online (Sandbox Code Playgroud)

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

zet*_*tic 12

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

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

使用虚拟属性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
Run Code Online (Sandbox Code Playgroud)


Bjo*_*orn 9

find_or_create_by_Rails内置了一个功能

# 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")
Run Code Online (Sandbox Code Playgroud)

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


Rob*_*rco 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
Run Code Online (Sandbox Code Playgroud)

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


Ell*_*iot 5

我最初提出的问题到最后变得非常扭曲。所以我要把它分开。

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

 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
Run Code Online (Sandbox Code Playgroud)

这将使您能够检查您的记录是否已经创建。您可以添加任何进一步的逻辑 if 语句。