Pregunta

Tengo un modelo de proyecto que acepta atributos anidados para la tarea.

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

La unicidad de validación en el modelo de tareas da un problema durante la actualización del proyecto.

En edición del proyecto elimino una tarea T1 y luego añadir una nueva tarea con el mismo nombre T1, la singularidad de validación restringe el ahorro de proyecto.

params mirada de hash algo así como

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

Validación en la tarea se realiza antes de la destrucción de la antigua tarea. De ahí la idea de validación fails.Any cómo validar tal que no se considera tarea a ser destruido?

¿Fue útil?

Solución

Andrew Francia creó un parche en este hilo , donde la validación se realiza en la memoria.

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

Otros consejos

A mi entender, el enfoque de Reiner sobre la validación en la memoria no sería práctico en mi caso, ya que tengo un montón de "libros", 500K y en crecimiento. Eso sería un gran éxito si se quiere llevar a todos en la memoria.

La solución que se me ocurrió es:

Coloque la condición de unicidad en la base de datos (que he encontrado es siempre una buena idea, como en mi experiencia Carriles no siempre hacer un trabajo bueno aquí) añadiendo lo siguiente a su archivo de migración en db / migrate /:

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

En el controlador, coloque la parada o update_attributes dentro de una transacción, y rescatar la excepción de base de datos. Por ejemplo.,

 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

end

Para rieles 4.0.1, este número se marca como siendo fijado por la presente solicitud de extracción, https: //github.com/rails/rails/pull/10417

Si usted tiene una tabla con un índice de campo único, y marcar un registro para la destrucción, y se genera un nuevo registro con el mismo valor que el campo único, a continuación, cuando se llama a salvar, un índice único nivel de base de datos será lanzado de error.

En lo personal esto todavía no funciona para mí, así que no creo que se fija por completo todavía.

La respuesta de Rainer Bendición es buena. Pero es mejor cuando podemos marcar las tareas que se duplican.

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

este

¿Por qué no utilizar: ámbito de aplicación

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

Esto creará tarea única para cada proyecto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top