web-dev-qa-db-fra.com

Comment les clauses Rails ActiveRecord chain "where" sans requêtes multiples?

Je suis un développeur PHP apprenant la beauté de Ruby on Rails, j'aime ActiveRecord et j'ai remarqué quelque chose de vraiment intéressant, c'est ainsi que les méthodes ActiveRecord détectent la fin de la chaîne de méthodes pour exécuter la requête.

@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

Comment fonctionne cette sorcellerie?

68
Ryan

La méthode where renvoie un ActiveRecord::Relation objet, et par lui-même cet objet n'émet pas de requête de base de données. C'est vous utilisez cet objet qui compte.

Dans la console, vous faites probablement ceci:

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

Et puis blammo il émet une requête de base de données et retourne ce qui semble être un tableau de tout le monde nommé Jason. Oui, Active Record!

Mais alors vous faites quelque chose comme ça:

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

Et puis cela émet une autre requête, mais celle-ci s'adresse aux personnes qui s'appellent Jason qui ont 26 ans. Mais elle émet uniquement une requête ne, alors où est passée l'autre requête?


Comme d'autres l'ont suggéré, cela se produit car la méthode where renvoie un objet proxy. Il n'exécute pas réellement une requête et ne renvoie pas un ensemble de données à moins qu'on ne lui demande de le faire.

Lorsque vous exécutez n'importe quoi dans la console, cela produira la version inspectée du résultat de tout ce que vous avez exécuté. Si vous mettez 1 dans la console et appuyez sur Entrée, vous obtiendrez 1 retour parce que 1.inspect est 1. La magie! De même pour "1". Une variété d'autres objets n'ont pas de méthode inspect définie et donc Ruby revient à celui de Object qui retourne quelque chose effroyable comme <Object#23adbf42560>.

Chacun ActiveRecord::Relation l'objet a la méthode inspect définie dessus afin qu'il provoque une requête. Lorsque vous écrivez la requête dans votre console, IRB appellera inspect sur la valeur de retour de cette requête et produira quelque chose de presque lisible par l'homme, comme le tableau que vous verriez.


Si vous veniez de publier ceci dans un script Ruby script standard, aucune requête ne serait exécutée tant que l'objet n'aurait pas été inspecté (via inspect) ou a été itéré via each, ou avait le to_a méthode appelée.

Jusqu'à ce que l'une de ces trois choses se produise, vous pouvez enchaîner autant d'instructions where que vous le souhaitez, puis lorsque vous faites appelez inspect, to_a ou each dessus, il exécutera finalement cette requête.

144
Ryan Bigg

Il existe un certain nombre de méthodes connues sous le nom de "kickers" qui déclenchent la requête vers la base de données. Avant cela, ils créent simplement AST, qui une fois lancés, généreront le SQL réel (ou le langage en cours de compilation) et exécuter la requête.

Voir ce billet de blog pour une explication plus approfondie de la façon dont cela est fait.

7
x1a4

Vous pouvez lire le code, mais un concept ici est le modèle de proxy.

@Person n'est probablement pas le véritable objet mais un proxy de cet objet et lorsque vous avez besoin d'un attribut, l'enregistrement actif exécute finalement la requête. Hibernate a le même concept.

4
Tiago Peczenyj