19

Task のネストされた属性を受け入れる Project モデルがあります。

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 で新しいタスクを追加すると、一意性の検証によりプロジェクトの保存が制限されます。

params ハッシュは次のようになります

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 に答える
-3

これを参照

:scope を使用しない理由

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

これにより、プロジェクトごとに固有のタスクが作成されます。

于 2010-05-05T12:37:53.407 に答える