Question

J'ai un modèle de projet qui accepte les attributs imbriqués pour la tâche.

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

Validation Unicité dans le modèle de travail donne problème lors de mise à jour du projet.

En édition de projet supprimer une tâche T1, puis ajouter une nouvelle tâche avec le même nom T1, validation unique limite l'économie du projet.

params hachage ressemble à quelque chose comme

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

Validation sur la tâche se fait avant de détruire la tâche ancienne. Par conséquent idée de validation fails.Any comment valider telle qu'elle ne considère pas la tâche à détruire?

Était-ce utile?

La solution

Andrew France a créé un patch dans cette fil , où la validation se fait en mémoire.

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

Autres conseils

Si je comprends bien, l'approche de Reiner sur la validation en mémoire ne serait pas pratique dans mon cas, comme je l'ai beaucoup de « livres », 500K et de plus en plus. Ce serait un grand succès si vous voulez apporter tout en mémoire.

La solution que je suis venu avec est à:

Placez la condition unique dans la base de données (que j'ai trouvé est toujours une bonne idée, comme dans mon expérience Rails ne fait pas toujours un bon travail ici) en ajoutant ce qui suit à votre fichier de migration dans db / migrate /:

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

Dans le contrôleur, placez la sauvegarde ou update_attributes dans une transaction, et sauver l'exception de base de données. Par exemple.

 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

Pour Rails 4.0.1, cette question est marqué comme étant fixé par cette demande de traction, https: //github.com/rails/rails/pull/10417

  

Si vous avez une table avec un index de champ unique, et vous marquez un enregistrement   pour la destruction, et vous construisez un nouveau record avec la même valeur que la   champ unique, puis lorsque vous appelez enregistrer, un niveau de base de données index unique   erreur sera levée.

Personnellement, cela ne fonctionne toujours pas pour moi, donc je ne pense pas qu'il est encore complètement fixé.

La réponse de Rainer Blessing est bonne. Mais il vaut mieux quand on peut marquer les tâches sont dupliqués.

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

cette

Pourquoi utilisez-vous pas: champ

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

cela va créer la tâche unique pour chaque projet.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top