validates_uniqueness_of in verschachtelter Modell Schienen zerstört
-
03-10-2019 - |
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?
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
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.