Domanda

Ho un modello di progetto che accetta gli attributi nidificati per 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

convalida unicità nel modello Task dà problema durante l'aggiornamento del progetto.

In Modifica del progetto elimino un T1 compito e quindi aggiungere una nuova attività con lo stesso nome T1, convalida l'unicità limita il risparmio di progetto.

params hash aspetto qualcosa di simile

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

La convalida il compito è fatto prima di distruggere il vecchio compito. Da qui la convalida fails.Any idea di come convalidare tale che essa non considera compito di essere distrutto?

È stato utile?

Soluzione

Andrew France ha creato una patch in questa filo , in cui la validazione è fatto in 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

Altri suggerimenti

A quanto mi risulta, l'approccio di Reiner sulla convalida in memoria non sarebbe pratico nel mio caso, come ho un sacco di "libri", 500K e in crescita. Questo sarebbe un grande successo se si vuole portare tutto in memoria.

La soluzione mi è venuta è quello di:

Inserisci la condizione unicità nel database (che ho trovato è sempre una buona idea, come nella mia esperienza Rails non sempre fare un lavoro bene qui) aggiungendo quanto segue al file di migrazione in db / migrate /:

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

Nel controllore, posizionare il Salva o update_attributes all'interno di una transazione, e salvare l'eccezione del database. Per es.,

 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

Per Rails 4.0.1, questo problema viene contrassegnato come fissato dalla presente richiesta di pull, https: //github.com/rails/rails/pull/10417

  

Se si dispone di una tabella con un indice di campo univoco, e segnare un record   per la distruzione, e si crea un nuovo record con lo stesso valore come il   campo univoco, quindi quando si chiama salvare, un indice univoco a livello di database   sarà gettato errore.

non lo fa personalmente questo ancora lavoro per me, quindi non credo che sia ancora completamente risolto.

La risposta di Rainer Blessing è buona. Ma è meglio quando siamo in grado di segnare quali compiti sono duplicati.

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

questo

Perché non si usa: portata

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

questo creerà unica operazione per ogni progetto.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top