Ich habe eine Situation gefunden, in der ActiveRecord scheinbar überflüssige Kinderdatensätze überprüft. Entschuldigung im Voraus für die Länge, da dies ziemlich komplex ist.ActiveRecord scheint unnötige Änderungen der untergeordneten Datensätze zu bestätigen
Hierbei handelt es sich um Verknüpfungen, die zuvor verwendet, aber in keiner Weise geändert wurden. Es tritt bei 3.2 bis zum letzten Master auf. Ich bin mir nicht sicher, ob es eine Design-Entscheidung ist, die zu unerwartetem Verhalten oder einem Fehler irgendeiner Art geführt hat.
ich einen Testfall aus dem eigentlichen Code reduziert sich wie folgt:
Modelle:
class A < ActiveRecord::Base
belongs_to :b
has_many :cs, :through => :b
before_validation { puts "A" }
end
class B < ActiveRecord::Base
has_many :as
has_many :cs
before_validation { puts "B" }
end
class C < ActiveRecord::Base
belongs_to :b
before_validation { puts "C" }
end
Migration:
class AddABC < ActiveRecord::Migration
def change
create_table :as do |t|
t.references :b
end
create_table :bs do |t|
end
create_table :cs do |t|
t.references :b
end
end
end
Der reduzierte Testfall, der es auslöst, ist dies bei der Ausführung in einer leeren Datenbank:
b = B.create!
c = C.create!
b.cs << c
a = A.new
a.b = b
a.cs.first
puts "X"
a.valid?
die Ausgabe gibt:
B
C
C
X
A
C
Welche, dass ein A seine Cs validiert Validierung zeigt.
Nun nachdem ich in diese ich die Option has_many :validate => false
berücksichtigt habe, und das Problem damit verschwindet. Aber mir scheint, dass hier mehr vor sich geht als hier - ertragen Sie mit mir.
Die AR docs say:
: validieren Wenn falsch, nicht die zugehörigen Objekte überprüfen, wenn das übergeordnete Objekt zu speichern. standardmäßig true
Aber ich finde das verwirrend, da dies eindeutig nicht alle Datensätze bedeuten kann. Es wird die Objekte nicht validieren, wenn ich nie die Verbindung bekomme (entferne a.cs.first
aus dem obigen Code), oder ich bekomme es, aber benutze es nie (ersetze es mit a.cs
). Dies ist, weil es durch validate_collection_association
in lib/active_record/autosave_association.rb
geht, die den Code enthält:
def validate_collection_association(reflection)
if association = association_instance_get(reflection.name)
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
records.each_with_index { |record, index| association_valid?(reflection, record, index) }
end
end
end
Es ist alles davon abhängig association_instance_get
die von dem Verband Cache abruft. Kein Cache bedeutet, dass keine Datensätze zu validieren sind.
Ich habe versucht, eine einfachere has_many zu machen, indem ich nur ein B-Modell aufstellt, das A referenziert, aber dann muss ich das B vor dem A erstellen, dann wird A nicht mehr ein neuer Datensatz sein, wenn ich es versuche es, und dieser Code verhindert, dass das Problem, da der Zweig speichern nicht mehr genannt wird die erste sein:
def associated_records_to_validate_or_save(association, new_record, autosave)
if new_record
association && association.target
elsif autosave
association.target.find_all(&:changed_for_autosave?)
else
association.target.find_all(&:new_record?)
end
end
die einzige wirkliche Erklärung, die ich mit oben kommen kann für sie nur geladene Datensätze validiert ist, weil die Absicht von Active hier dient nur dazu, geänderte Datensätze zu validieren. Wirklich würde ich erwarten, dass es validiert, wenn und nur wenn es sparen wird, und folglich die Standardautosave-Option, nur geänderte Aufzeichnungen zu speichern, eine Gültigkeitsprüfung verhindern sollte.
Ich habe eine related ticket gefunden und die commit 27aa4dda7d89ce733 (noch nicht in irgendeiner Version denke ich), die eine Änderung vornimmt, aber dieses spezifische Problem von meinen Tests nicht behebt.Es enthält jedoch den Ausdruck:
!record.persisted? || record.changed? || record.marked_for_destruction?
und wenn ich diesen Zustand zu der innersten Schleife von validate_collection_association
fügen Sie dann das Problem verschwindet, wobei die Active Tests immer noch auf meiner Maschine vorbei.
Dies war ein bedeutendes Leistungsproblem in meinem Projekt, weil das fragliche Modell nur im Admin validiert werden sollte, wo ein nicht indizierten Feld in einer benutzerdefinierten Validierung akzeptiert wurde aufgrund der Seltenheit der Speicherung und damit ich beurteilten, dass die Indizierung überindizieren würde (es wäre nicht nur ein Feld). Offensichtlich ist diese Übervalidierung in den meisten Fällen viel weniger ernst und scheint nur in einem speziellen Fall zu passieren, so dass dies ein Fehler sein könnte.
So, während ich eine gute Idee habe, was passiert, bin ich nicht ganz sicher, was passieren sollte, weshalb ich dies nicht als ActiveRecord Ticket eingereicht habe. Denkst du, das ist ein Fehler? Warum funktioniert es so? Wofür ist die Validierungsoption wirklich? Wenn das ein Fehler ist, können Sie erklären, warum der Code so funktioniert und warum er übergreift? In welchem Fall würde mein Code zu ActiveRecord über Break wechseln?
Ich wäre interessiert zu sehen, wenn Hinzufügen von inverse_of: auf den Assoziationen dieses Verhalten überhaupt geändert. TBH Ich habe aber keine große Hoffnung, dass es so wäre. –
In meiner Prüfung tat es nicht. –