views:

367

answers:

2

I have a Project model which accepts nested attributes for Task.

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

Uniqueness validation in Task model gives problem while updating Project.

In edit of project i delete a task T1 and then add a new task with same name T1, uniqueness validation restricts the saving of Project.

params hash look something like

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

Validation on task is done before destroying the old task. Hence validation fails.Any idea how to validate such that it doesn't consider task to be destroyed?

A: 

Ref this

Why don't you use :scope

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

this will create unique Task for each project.

Salil
scope => 'project_id' will work for different project ids, but in the above case project_id is same. I need to destroy a task then add a new task with same name for a project.
arun
will you please paste your code here so that one could help you.
Salil
In controllerdef update @project = Project.find(params[:id]) @project.update_attributes(params[:project])endModel code is same as i have mentioned above.I have done exactly similar to as described by Ryan Bates in railscasts episode 197
arun
A: 

Andrew France created a patch in this thread, where the validation is done in memory.

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
RainerB
Thanks RainerB, this solves my problem.
arun