2015-01-06 12 views
9

Ich bin eine Vereinigung von einem instantiierten Objekt zu eifrig Last versuchen, das heißt, anstatt die Verbände zusammen mit Eltern-Objekt zu lesen ...Rails 4 eifrig Last für ein Objekt

User.includes(:characters).first 

... aufzuschieben, bis ich entscheide, es wirklich benötigt wird und so etwas wie:

u = User.first 
# Other stuff ... 
u.eager_load(:characters) 

In Rails 3 I Active mit dieser Methode erweitert:

def eager_load(*args) 
    ActiveRecord::Associations::Preloader.new(self, *args).run 
end 

Und es hat gut funktioniert. Schienen 4 änderten diesen Teil ein wenig und ich aktualisierte die Methode zu:

Leider tut es jetzt etwas seltsam. Werfen Sie einen Blick auf:

2.1.2 :001 > u = User.first 
[2015-01-06 23:18:03] DEBUG ActiveRecord::Base : User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1 
=> #<User id: 1, ...> 
2.1.2 :002 > u.eager_load :characters 
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters` WHERE `characters`.`user_id` IN (1) 
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.3ms) SELECT `characters`.* FROM `characters` 
[2015-01-06 23:18:07] DEBUG ActiveRecord::Base : Character Load (0.2ms) SELECT `characters`.* FROM `characters` 
=> [#<ActiveRecord::Associations::Preloader::HasMany:0x00000007c26d28 @klass=Character(id: integer, ...), @owners=[#<User id: ...], @reflection=#<ActiveRecord::Reflection::HasManyReflection:0x0000000496aa60 @name=:characters, ...(LOTS of stuff here)...] 

Beachten Sie insbesondere die doppelte SELECT aller Datensätze. Gibt es eine Möglichkeit, dieses Verhalten oder eine andere Methode zu reparieren, um das zu tun, was ich will?

+1

In Ihrem Beispiel, warum möchten Sie nicht die Assoziationen zusammen mit dem übergeordneten Objekt lesen? Und warum kannst du einfach 'u.characters' nennen - wird das der Fall sein, wenn du die Charaktere nur dann bekommst, wenn du sie brauchst? – Nona

+0

Dies war nur um das Problem zu demonstrieren. Ich möchte möglicherweise eine tiefere Struktur (z. B. "characters: {items:: attributes}") vorladen, um N + 1-Abfragen zu verhindern. –

+3

Ich kann den Anwendungsfall nicht verstehen, bei dem 'includes (Zeichen: {items:: attributes})' nicht ausreichend ist? – omarvelous

Antwort

1

hatte ich das gleiche Problem und Tracing meine Schritte zurück bemerkte ich, Dies könnte ein Artefakt bei der Ausführung dieses Codes auf der Konsole sein. Jeder Ausdruck wird ausgewertet, aber wir sind nur in der Seite interessiert: Ist dies der Fall Versuch ist:

u.eager_load(:characters); nil 

Ich hoffe, dass es helfen, ich bin zur Zeit daran gearbeitet, so ist dies meine einzige Beobachtung so weit .

+0

Incredible. Ja, das hilft und unnötige Abfragen sind weg. Und ja, ich habe überprüft, dass dies in Serverprotokollen überhaupt nicht passiert ist. Aber ich kann nicht verstehen, was hier passiert. * Was * wird mit und ohne '; Nil' ...? –

+0

Meine (gebildete) Vermutung ist, dass das Preloader-Objekt (zurückgegeben beim Aufruf von '.preload') einem' ActiveRecord :: Relation' (Beziehung von jetzt an) ähnlich ist. Wenn Sie in der Konsole eine Beziehung Schritt für Schritt erstellen, wird die Abfrage jedes Mal ausgeführt (einfach weil jede Anweisung in der Konsole zurückgegeben wird), aber auf dem Server erstellen Sie eine Beziehung und führen sie nur aus, wenn Sie die Ergebnisse "anfordern" (durch sie hindurchschleifen usw.). Nur um klar zu sein, brauchen Sie nicht die '; nicht auf dem Server, nur die Konsole. – Leito

0

Um Doppelabfragen zu vermeiden, sollten Sie wirklich alles, was Sie denken, dass Sie verwenden werden, eifrig laden. Also, sollten Sie nisten Einschluss- Befehl wie in diesem Beispiel auf dem documentation:

users = User.includes(:address, friends: [:address, :followers]) 

Sie können auch für noch tiefere Beziehungen wie:

users = User.includes(:address, {friends: [:address, :followers]}) 
+0

Dies ist genau das, was ich vermeiden möchte - Ich möchte ladungsabhängige Objekte verschieben, bis es sich herausstellt, dass ich sie brauche. –

+0

Also, das einzige, was Sie brauchen, ist sie von der Assoziationsmethode zu nennen: User.first.address zum Beispiel. – Hamdan

+1

@ Hamdam, er verwendet nicht wirklich einen einzigen Benutzer, aber viele; in Ihrem Beispiel, ohne eifriges Laden N zusätzliche Abfragen werden Laden der Adresse – Leito

Verwandte Themen