2012-05-25 11 views
52

Sono uno sviluppatore PHP che apprende la grandezza di Ruby on Rails, sto amando ActiveRecord e ho notato qualcosa di veramente interessante, che è come i metodi ActiveRecord rilevano la fine della catena di metodi per eseguire la query.In che modo Rails ActiveRecord concatena le clausole "where" senza più query?

@person = Person.where(name: 'Jason').where(age: 26) 

# In my humble imagination I'd think that each where() executes a database query 
# But in reality, it doesn't until the last method in the chain 

Come funziona questa stregoneria?

+0

E non lo fa presso l'ultimo metodo. Non ha modo di saperlo. Si consideri 'x = Person.where (..); @person = x.where (..) ', che dovrebbe essere identico. Lo fa qualche tempo dopo, quindi qual è il grilletto? ;-) –

risposta

122

Il metodo where restituisce un oggetto ActiveRecord::Relation e da solo questo oggetto non emette una query di database. È dove si utilizza questo oggetto che conta.

Nella console, probabilmente stai facendo questo:

@person = Person.where(name: "Jason") 

E poi blammo emette una query di database e restituisce quello che sembra essere un array di tutti di nome Jason. Sì, Active Record!

Ma poi si fa qualcosa di simile:

@person = Person.where(name: "Jason").where(age: 26) 

E poi che emette un'altra query, ma questo è per le persone che sono chiamati Jason che sono 26. Ma è solo l'emissione di una di query, quindi dove 'l'altra query va?


Come altri hanno suggerito, questo sta accadendo perché il metodo where restituisce un oggetto proxy. In realtà non esegue una query e restituisce un set di dati a meno che non venga richiesto di farlo.

Quando si esegue nella console, verrà emessa la versione ispezionata del risultato di qualunque cosa si è eseguito. Se inserisci 1 nella console e premi Invio, riceverai 1 perché 1.inspect è 1. Magia! Lo stesso vale per "1". Una varietà di altri oggetti non ha un metodo inspect definito e quindi Ruby ricade su quello Object che restituisce qualcosa spettrale come <Object#23adbf42560>.

Ogni singolo oggetto ActiveRecord::Relation ha il metodo inspect definito su di esso in modo che causi una query. Quando scrivi la query nella tua console, IRB chiamerà inspect sul valore restituito da quella query e genererà qualcosa di quasi umano leggibile, come l'array che vedresti.


Se tu fossi solo il rilascio della presente in uno script standard di Ruby, allora nessuna query sarebbe eseguito fino a quando l'oggetto è stato ispezionato (via inspect) o è stato iterato attraverso l'utilizzo di each, o ha avuto il metodo to_a chiamato su di esso.

Fino a una di quelle tre cose accadono, è possibile catena come molti where dichiarazioni su come vi piace e poi quando si fare chiamata inspect, to_a o each su di esso, allora sarà finalmente eseguire tale query.

+1

Divertente e informativo! Grazie! – henrebotha

+1

Rilevante [Railscast] (http://railscasts.com/episodes/239-activerecord-relation-walkthrough) che lo spiega con il codice sorgente Rails. – flexus

+0

mente soffiata ...... – sixty4bit

4

È possibile leggere il codice, ma un concetto qui è il modello proxy.

Probabilmente @person non è l'oggetto reale ma un proxy per questo oggetto e quando è necessario un attributo, il record attivo esegue infine la query. Hibernate ha lo stesso concetto.

6

Esistono numerosi metodi noti come "kicker" che attivano effettivamente la query sul database. Prima di ciò, creano solo nodi AST, che, una volta avviati, generano l'effettivo SQL (o la lingua in fase di compilazione) ed eseguono la query.

Vedere this blog post per una spiegazione più dettagliata di come viene eseguita.

-1

Forse po 'troppo tardi, ma è possibile utilizzare un hash:

@person = Person.where({name: "Jason", age: 26}) 

risultante query:

SELECT "person".* FROM "person" WHERE "person"."name" = 'Jason' AND "person"."age" = 26 
Problemi correlati