Frage

Ich habe ein Projekt Modell, das für Task-verschachtelte Attribute akzeptiert.

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

Einmaligkeit Validierung im Task-Modell gibt Problem beim Projekt zu aktualisieren.

In Bearbeitung von project i eine Aufgabe T1 löschen und dann eine neue Aufgabe mit demselben Namen T1 hinzufügen, Einzigartigkeit Validierung schränkt die Einsparung von Projekt.

params-Hash-Look so etwas wie

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

Validierung auf Aufgabe erfolgt vor der alte Aufgabe zu zerstören. Daher Validierung fails.Any Idee, wie diese zu bestätigen, dass es nicht Aufgabe zerstört wird nicht berücksichtigt?

War es hilfreich?

Lösung

erstellt Andrew Frankreich einen Patch in diesem Thread , wo die Validierung wird im Speicher durchgeführt.

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

Andere Tipps

Wie ich es verstehe, Reiner Ansatz etwa in Speicher Validierung in meinem Fall nicht praktikabel wäre, wie ich eine Menge „Bücher“ habe, 500K und wachsen. Das wäre ein großer Erfolg, wenn Sie alle in den Speicher bringen wollen.

Die Lösung kam ich mit ist:

Legen Sie den Einzigartigkeit Zustand in der Datenbank (die ich gefunden habe, ist immer eine gute Idee, wie es in meiner Erfahrung Rails tut nicht immer einen guten Job hier), indem Sie den folgenden in der Migrationsdatei in db / migrate /:

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

In der Steuerung legen Sie das Speichern oder update_attributes innerhalb einer Transaktion, und die Datenbank Ausnahme retten. Z.B.

 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

Ende

Für Rails 4.0.1 ist dieses Problem gekennzeichnet durch diese Pull-Anforderung festgelegt ist, https: //github.com/rails/rails/pull/10417

Wenn Sie eine Tabelle mit einem eindeutigen Feldindex haben, und markieren Sie einen Datensatz für die Zerstörung, und Sie bauen einen neuen Datensatz mit dem gleichen Wert wie die eindeutiges Feld, dann, wenn Sie speichern rufen, eine Datenbankebene eindeutigen Index Fehler werden geworfen werden.

persönlich noch dies nicht funktioniert für mich, so dass ich glaube nicht, dass es vollständig noch festgelegt wird.

Rainer Blessing Antwort ist gut. Aber es ist besser, wenn wir markieren können, welche Aufgaben dupliziert werden.

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

dieser

Warum Sie nicht verwenden: scope

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

Dies wird für jedes Projekt einzigartig Aufgabe erstellen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top