ActiveRecord scheint ungültige Kinddatensätze unnötigerweise zu validieren

9

Ich habe eine Situation gefunden, in der ActiveRecord scheinbar unnötige Kinderdatensätze überprüft. Entschuldigung im Voraus für die Länge, da dies ziemlich komplex ist.

Dies bezieht sich auf Verbindungen, 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 Designentscheidung ist, die zu unerwartetem Verhalten oder einem Fehler irgendeiner Art geführt hat.

Ich habe einen Testfall wie folgt vom eigentlichen Code reduziert:

Modelle:

%Vor%

Migration:

%Vor%

Der reduzierte Testfall, der dies auslöst, ist dies, wenn er in einer leeren Datenbank ausgeführt wird:

%Vor%

was gibt output:

%Vor%

Was zeigt, dass die Validierung eines A seine Cs validiert.

Nachdem ich mir nun die Option has_many :validate => false angesehen habe, ist das Problem verschwunden. Aber mir scheint, dass hier mehr vor sich geht als hier - ertrage mit mir.

Die AR-Dokumente sagen :

  

: validate Wenn false, validieren Sie die verknüpften Objekte nicht, wenn Sie das übergeordnete Objekt 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 Assoziation bekomme (entferne a.cs.first von dem obigen Code), oder ich bekomme es, aber benutze es nie (ersetze durch a.cs ). Dies liegt daran, dass es validate_collection_association in lib/active_record/autosave_association.rb durchläuft, was den Code enthält:

%Vor%

Alles hängt von association_instance_get ab, das vom Assoziations-Cache abgerufen wird. Kein Cache bedeutet, dass keine Datensätze zu validieren sind.

Ich habe versucht, ein einfacheres has_many zu machen, indem ich nur ein B-Modell aufstelle, das auf A verweist, aber dann muss ich das B vor dem A erstellen, dann wird A nicht mehr ein neuer Datensatz sein, wenn ich es versuche Speichern Sie es, und dieser Code verhindert das Problem, da die aufgerufene Verzweigung nicht mehr die erste sein wird:

%Vor%

Die einzige wirkliche Erklärung, die ich dafür finden kann, nur geladene Datensätze zu validieren, liegt darin, dass die Absicht von ActiveRecord darin besteht, nur geänderte Datensätze zu validieren. Wirklich würde ich erwarten, dass es validiert, wenn und nur wenn es speichern wird, und deshalb die Standardautosave-Option, nur geänderte Aufzeichnungen zu speichern, eine Bestätigung verhindern sollte.

Ich habe ein damit verbundenes Ticket und das Commit 27aa4dda7d89ce733 (noch nicht in irgendeiner Version, denke ich), die eine Änderung vornimmt, aber dieses spezielle Problem nicht aus meinen Tests löst. Es enthält jedoch den Ausdruck:

%Vor%

und wenn ich diese Bedingung zu der innersten Schleife von validate_collection_association hinzufüge, dann verschwindet das Problem, mit den ActiveRecord-Tests, die immer noch meinen Rechner weitergeben.

Dies war ein bedeutendes Leistungsproblem in meinem Projekt, weil das fragliche Modell nur im Admin validiert werden sollte, wo ein nicht indiziertes Feld, das in einer benutzerdefinierten Validierung verwendet wurde, aufgrund der Seltenheit der Speicherung akzeptabel war und daher beurteilt wurde Bei der Indizierung würde es sich um eine Indizierung handeln (es wäre nicht nur ein Feld). Offensichtlich ist diese Überbewertung in den meisten Fällen viel weniger ernst und scheint nur in einem ganz bestimmten Fall zu passieren, so dass dies ein Fehler sein könnte.

Obwohl ich eine gute Idee habe, was passiert, bin ich nicht ganz sicher, was passiert, 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 brechen ändern?

    
matthew.tuck 27.10.2015, 13:07
quelle

1 Antwort

3

Der Grund dafür ist, dass die Beziehung zwischen A und C durch B besteht.

Bevor Sie a.b = b zuweisen, hat a keine bs oder cs .

Wenn Sie a.b = b zuweisen, aber nicht a.cs , dann hat a keinen Grund zu versuchen, die zugehörige cs zu laden. has_many erstellt nur die Convenience-Methode cs , sie wird nicht für Sie aufgerufen. Hier wird nur a.b_id auf b.id gesetzt.

Wenn Sie a.cs aufrufen, sucht a nach verknüpften cs -Objekten bis b , da b verfügbar ist. Es findet diese Objekte und fügt sie als untergeordnete Elemente zu a hinzu.

Ich sehe Ihren Punkt, dass technisch gesehen in diesem speziellen Fall in diesem speziellen Schema für das cs nichts zu tun ist, aber ich kann sehen, warum ActiveRecord prüft. Diese Objekte sind, soweit sie betroffen sind, untergeordnete Elemente von a , und untergeordnete Datensätze werden validiert, sofern nicht ausdrücklich angegeben wird, dass sie nicht durch validate: false ersetzt werden sollen.

In diesem Fall ist a ein Kind von b , daher ist a nicht erforderlich, um es zu validieren.

Im Allgemeinen werden die Eltern die entsprechenden Kinder validieren lassen. Kinder müssen ihre Eltern nicht validieren.

    
Bassel Samman 05.11.2015 00:38
quelle