Comment exécuter du SQL brut dans l'application Flask-SQLAlchemy


219

Comment exécutez-vous le SQL brut dans SQLAlchemy?

J'ai une application web python qui s'exécute sur le flacon et s'interface avec la base de données via SQLAlchemy.

J'ai besoin d'un moyen d'exécuter le SQL brut. La requête implique plusieurs jointures de table ainsi que des vues en ligne.

J'ai essayé:

connection = db.session.connection()
connection.execute( <sql here> )

Mais je continue à recevoir des erreurs de passerelle.


5
J'ai déjà examiné cela, mais je n'ai pas trouvé de tutoriel sur l'exécution d'une mise à jour. Je préfère également ne pas apprendre la syntaxe et convertir une requête SQL assez longue (environ 20 lignes).
starwing123

103
@MarkusUnterwaditzer J'avais l'habitude de penser cela, mais maintenant je suis fortement en désaccord. Le SQL brut et correctement paramétré est généralement beaucoup plus facile à lire et à gérer qu'un groupe d'appels de fonction et d'objets qui le génèrent. Il vous offre également toutes les capacités de la base de données sans avoir à passer par des cercles pour que l'ORM génère la syntaxe correcte (si c'est même possible) et empêche l'ORM de faire des choses inattendues. Vous pourriez vous poser la question "Alors pourquoi utiliser SQLAlchemy?", Et la seule réponse que j'ai est: "L'application existante l'utilise et tout changer est trop cher."
jpmc26

4
@ jpmc26 A augmenté votre commentaire - en tant qu'amant de SQL, j'ai du mal à l'idée de "donner les clés de la base de données" à un alchimiste irresponsable et j'ai tendance à me pencher du côté de l' ORM est un contre-modèle :) a déclaré que je souhaitais accélérer certains composants, tels que l'enregistrement / la gestion des utilisateurs, ainsi que la génération de tableaux avec des séquences de boutons pour lesquels je peux coder les actions + SQL. Avez-vous rencontré des outils compatibles avec les sceptiques ORM qui fonctionnent bien pour vous dans un cadre Python?
zx81

@ jpmc26 Qu'utilisez-vous dans un framework Python pour utiliser uniquement SQL ou assez proche comme C # Dapper? Tout ce que je vois dans un framework web Python veut que j'utilise SQLAlchemy, et je n'aime pas un ORM, et si j'en utilise un, il est extrêmement minime.
johnny

@johnny Je n'ai pas eu l'occasion de l'essayer moi-même, mais les bibliothèques de connexion de base de données brutes sont probablement suffisantes. Par exemple, psycopg2 a des curseurs qui renvoient namedtupleet dictdirectement: initd.org/psycopg/docs/extras.html .
jpmc26

Réponses:


310

As-tu essayé:

result = db.engine.execute("<sql here>")

ou:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names

7
Si vous effectuez une insertion ou une mise à jour, comment validez-vous la transaction?
David S

14
Si vous utilisez du SQL brut, vous contrôlez les transactions, vous devez donc émettre les instructions BEGINet COMMITvous-même.
Miguel

1
Les mêmes commandes SQL fonctionnent-elles lorsque vous les exécutez sans SQLAlchemy? Vous souhaiterez peut-être activer le débogage sur votre base de données afin de voir quelles commandes elle exécute.
Miguel

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))l'exécute et le valide également.
Devi

8
@Miguel "Si vous utilisez du SQL brut, vous contrôlez les transactions, vous devez donc émettre vous-même les instructions BEGIN et COMMIT." Ce n'est tout simplement pas vrai. Vous pouvez utiliser du SQL brut avec un objet de session. Je viens de remarquer ce commentaire, mais vous pouvez voir ma réponse pour savoir comment utiliser une session avec du SQL brut.
jpmc26

180

Les objets de session SQL Alchemy ont leur propre executeméthode:

result = db.session.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

Toutes vos requêtes d'application doivent passer par un objet session, qu'elles soient SQL brut ou non. Cela garantit que les requêtes sont correctement gérées par une transaction , ce qui permet à plusieurs requêtes dans la même demande d'être validées ou annulées en une seule unité. Sortir de la transaction en utilisant le moteur ou la connexion vous expose à un risque beaucoup plus important de bogues subtils, éventuellement difficiles à détecter, qui peuvent vous laisser des données corrompues. Chaque demande doit être associée à une seule transaction, et l'utilisation db.sessiongarantira que c'est le cas pour votre application.

Notez également qu'il executeest conçu pour les requêtes paramétrées . Utilisez des paramètres, comme :valdans l'exemple, pour toutes les entrées de la requête pour vous protéger des attaques par injection SQL. Vous pouvez fournir la valeur de ces paramètres en passant a dictcomme deuxième argument, où chaque clé est le nom du paramètre tel qu'il apparaît dans la requête. La syntaxe exacte du paramètre lui-même peut être différente selon votre base de données, mais toutes les principales bases de données relationnelles les prennent en charge sous une forme ou une autre.

En supposant que c'est une SELECTrequête, cela retournera un itérable d' RowProxyobjets.

Vous pouvez accéder à des colonnes individuelles avec une variété de techniques:

for r in result:
    print(r[0]) # Access by positional index
    print(r['my_column']) # Access by column name as a string
    r_dict = dict(r.items()) # convert to dict keyed by column names

Personnellement, je préfère convertir les résultats en namedtuples:

from collections import namedtuple

Record = namedtuple('Record', result.keys())
records = [Record(*r) for r in result.fetchall()]
for r in records:
    print(r.my_column)
    print(r)

Si vous n'utilisez pas l'extension Flask-SQLAlchemy, vous pouvez toujours utiliser facilement une session:

import sqlalchemy
from sqlalchemy.orm import sessionmaker, scoped_session

engine = sqlalchemy.create_engine('my connection string')
Session = scoped_session(sessionmaker(bind=engine))

s = Session()
result = s.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

Un Select renverra un ResultProxy.
Alan B

@AlanB Oui. J'ai mal choisi mes mots quand je l'ai appelé une séquence, ce qui implique qu'il met en œuvre le protocole de séquence. J'ai corrigé et clarifié. Merci.
jpmc26

@ jpmc26 devrait fermer la session après avoir exécuté la requête comme db.session.close ()? Et aura-t-il toujours les avantages de la mise en commun des connexions?
ravi malhotra

58

docs: Didacticiel SQL Expression Language - Utilisation de texte

exemple:

from sqlalchemy.sql import text

connection = engine.connect()

# recommended
cmd = 'select * from Employees where EmployeeGroup = :group'
employeeGroup = 'Staff'
employees = connection.execute(text(cmd), group = employeeGroup)

# or - wee more difficult to interpret the command
employeeGroup = 'Staff'
employees = connection.execute(
                  text('select * from Employees where EmployeeGroup = :group'), 
                  group = employeeGroup)

# or - notice the requirement to quote 'Staff'
employees = connection.execute(
                  text("select * from Employees where EmployeeGroup = 'Staff'"))


for employee in employees: logger.debug(employee)
# output
(0, 'Tim', 'Gurra', 'Staff', '991-509-9284')
(1, 'Jim', 'Carey', 'Staff', '832-252-1910')
(2, 'Lee', 'Asher', 'Staff', '897-747-1564')
(3, 'Ben', 'Hayes', 'Staff', '584-255-2631')

1
Le lien vers les documents sqlalchemy semble être obsolète. Ceci est plus récent: docs.sqlalchemy.org/en/latest/core/…
Carl

1
Puis-je demander pourquoi nous utilisons ==?
Nam G VU

1
@Jake Berger un grand merci pour vous. J'ai perdu presque une journée à chercher cette réponse. J'étais juste en train d'exécuter directement le sql sans convertir en texte. C'était lancer une erreur chaque fois que nous avons% étudiants% dans ma clause where. Un grand applaudissement pour votre réponse.
Suresh Kumar

1
@NamGVU car comme dans la plupart des langages de programmation, il =est normalement réservé à l' attribution d' une valeur; alors qu'il ==est réservé pour comparer les valeurs
Jake Berger

2
@JakeBerger Avez-vous un lien pour cela? SQL n'est pas un tel langage, et à en juger par les documents SQLAlchemy, ce n'est pas le cas.
johndodo

36

Vous pouvez obtenir les résultats des requêtes SQL SELECT à l'aide de from_statement()et text()comme indiqué ici . Vous n'avez pas à gérer les tuples de cette façon. À titre d'exemple pour une classe Userayant le nom de table, usersvous pouvez essayer,

from sqlalchemy.sql import text
.
.
.
user = session.query(User).from_statement(
    text("SELECT * FROM users where name=:name")).\
    params(name='ed').all()

return user

15
result = db.engine.execute(text("<sql here>"))

exécute le <sql here>mais ne le valide pas sauf si vous êtes en autocommitmode. Ainsi, les insertions et les mises à jour ne se refléteraient pas dans la base de données.

Pour vous engager après les modifications, faites

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

2

Il s'agit d'une réponse simplifiée sur la façon d'exécuter une requête SQL à partir de Flask Shell

Tout d'abord, mappez votre module (si votre module / application est manage.py dans le dossier principal et que vous êtes dans un système d'exploitation UNIX), exécutez:

export FLASK_APP=manage

Run Flask shell

flask shell

Importez ce dont nous avons besoin ::

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
from sqlalchemy import text

Exécutez votre requête:

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

Cela utilise la connexion à la base de données actuellement qui a l'application.


0

Avez-vous essayé d'utiliser connection.execute(text( <sql here> ), <bind params here> )et de lier des paramètres comme décrit dans la documentation ? Cela peut aider à résoudre de nombreux problèmes de mise en forme des paramètres et de performances. Peut-être que l'erreur de passerelle est un délai d'attente? Les paramètres de liaison ont tendance à accélérer considérablement l'exécution des requêtes complexes.


2
selon les documents , ça devrait l'être connection.execute(text(<sql here>), <bind params> ). bind paramsne devrait PAS être dedans text(). alimentation des paramètres de liaison à la méthode execute ()
Jake Berger

Le lien de Jake est rompu. Je pense que c'est l'URL qui est pertinente maintenant: docs.sqlalchemy.org/en/latest/core/…
code_dredd

-1

Si vous voulez éviter les tuples, une autre méthode consiste à appeler les méthodes first, oneou all:

query = db.engine.execute("SELECT * FROM blogs "
                           "WHERE id = 1 ")

assert query.first().name == "Welcome to my blog"
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.