19

我有一个项目模型,它接受任务的嵌套属性。

class Project < ActiveRecord::Base  
  has_many :tasks

  accepts_nested_attributes_for :tasks, :allow_destroy => :true

end

class Task < ActiveRecord::Base  
validates_uniqueness_of :name end

任务模型中的唯一性验证在更新项目时会出现问题。

在项目编辑中,我删除了一个任务 T1,然后添加了一个同名的新任务 T1,唯一性验证限制了项目的保存。

参数哈希看起来像

task_attributes => { {"id" =>
"1","name" => "T1", "_destroy" =>
"1"},{"name" => "T1"}}

在销毁旧任务之前完成任务验证。因此验证失败。知道如何验证它不认为任务被破坏吗?

4

5 回答 5

17

Andrew France 在这个线程中创建了一个补丁,验证在内存中完成。

class Author
  has_many :books

  # Could easily be made a validation-style class method of course
  validate :validate_unique_books

  def validate_unique_books
    validate_uniqueness_of_in_memory(
      books, [:title, :isbn], 'Duplicate book.')
  end
end

module ActiveRecord
  class Base
    # Validate that the the objects in +collection+ are unique
    # when compared against all their non-blank +attrs+. If not
    # add +message+ to the base errors.
    def validate_uniqueness_of_in_memory(collection, attrs, message)
      hashes = collection.inject({}) do |hash, record|
        key = attrs.map {|a| record.send(a).to_s }.join
        if key.blank? || record.marked_for_destruction?
          key = record.object_id
        end
        hash[key] = record unless hash[key]
        hash
      end
      if collection.length > hashes.length
        self.errors.add_to_base(message)
      end
    end
  end
end
于 2010-05-21T14:54:04.137 回答
3

据我了解,Reiner 关于内存验证的方法对我来说并不实用,因为我有很多“书”,500K 并且还在增长。如果您想将所有内容都带入记忆中,那将是一个很大的打击。

我想出的解决方案是:

通过将以下内容添加到 db/migrate/ 中的迁移文件中,将唯一性条件放入数据库中(我发现这始终是一个好主意,因为根据我的经验,Rails 在这里并不总是做得很好):

  add_index :tasks [ :project_id, :name ], :unique => true

在控制器中,将 save 或 update_attributes 放在事务中,并挽救数据库异常。例如,

 def update
   @project = Project.find(params[:id])
   begin
     transaction do       
       if @project.update_attributes(params[:project])
          redirect_to(project_path(@project))
       else
         render(:action => :edit)
       end
     end
   rescue
     ... we have an exception; make sure is a DB uniqueness violation
     ... go down params[:project] to see which item is the problem
     ... and add error to base
     render( :action => :edit )
   end
 end

结尾

于 2013-08-04T20:34:36.347 回答
1

对于 Rails 4.0.1,此问题被标记为已通过此拉取请求修复,https://github.com/rails/rails/pull/10417

如果你有一个带有唯一字段索引的表,并且你标记了一条记录为销毁,并且你建立了一个与唯一字段值相同的新记录,那么当你调用 save 时,就会抛出一个数据库级别的唯一索引错误。

就个人而言,这仍然对我不起作用,所以我认为它还没有完全修复。

于 2013-12-04T18:34:48.107 回答
1

Rainer Blessing 的回答很好。但最好能标记哪些任务是重复的。

class Project < ActiveRecord::Base
  has_many :tasks, inverse_of: :project

  accepts_nested_attributes_for :tasks, :allow_destroy => :true
end

class Task < ActiveRecord::Base
  belongs_to :project

  validates_each :name do |record, attr, value|
    record.errors.add attr, :taken if record.project.tasks.map(&:name).count(value) > 1
  end
end
于 2016-08-31T10:29:01.093 回答
-3

参考这个

你为什么不使用 :scope

class Task < ActiveRecord::Base
  validates_uniqueness_of :name, :scope=>'project_id' 
end

这将为每个项目创建唯一的任务。

于 2010-05-05T12:37:53.407 回答