So führen Sie Raw SQL in der Flask-SQLAlchemy-App aus

218

Wie führt man Raw SQL in SQLAlchemy aus?

Ich habe eine Python-Web-App, die auf einer Flasche ausgeführt wird und über SQLAlchemy mit der Datenbank verbunden ist.

Ich brauche eine Möglichkeit, um das rohe SQL auszuführen. Die Abfrage umfasst mehrere Tabellenverknüpfungen zusammen mit Inline-Ansichten.

Ich habe es versucht:

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

Aber ich bekomme immer wieder Gateway-Fehler.

starwing123
quelle
5
Ich habe mir das schon einmal angesehen, aber ich konnte kein Tutorial zum Ausführen eines Updates finden. Ich möchte auch lieber nicht die Syntax lernen und eine ziemlich lange (ca. 20 Zeilen) SQL-Abfrage verdecken.
Starwing123
103
@ MarkusUnterwaditzer Früher habe ich das gedacht, aber jetzt bin ich völlig anderer Meinung. Rohes, richtig parametrisiertes SQL ist im Allgemeinen viel einfacher zu lesen und zu warten als eine Reihe von Funktionsaufrufen und Objekten, die es generieren. Es bietet Ihnen auch die vollen Funktionen der Datenbank, ohne durch die Rahmen springen zu müssen, damit der ORM die richtige Syntax generiert (falls dies überhaupt möglich ist), und verhindert, dass der ORM unerwartete Dinge tut. Sie könnten die Frage stellen: "Warum dann überhaupt SQLAlchemy verwenden?" Und die einzige Antwort, die ich habe, ist: "Die vorhandene Anwendung verwendet sie und das Ändern von allem ist zu teuer."
jpmc26
4
@ jpmc26 Ihr Kommentar wurde verbessert - als Liebhaber von SQL fällt es mir schwer, "die Schlüssel für die Datenbank an einen verantwortungslosen Alchemisten weiterzugeben", und ich neige dazu, mich auf die Seite von ORM zu stützen, ist ein Gegenmuster :) Das ist Ich würde gerne bestimmte Komponenten beschleunigen, z. B. die Benutzerregistrierung / -verwaltung sowie die Erstellung von Tabellen mit Schaltflächenfolgen, für die ich die Aktionen + SQL codieren kann. Sind Sie auf einige ORM-skeptische Tools gestoßen, die in einem Python-Framework für Sie gut funktionieren?
zx81
@ jpmc26 Was verwenden Sie in einem Python-Framework, um nur SQL oder ziemlich nahe wie C # Dapper zu verwenden? Alles, was ich in einem Python-Webframework sehe, möchte, dass ich SQLAlchemy verwende, und ich mag kein ORM, und wenn ich eines verwende, ist es äußerst minimal.
Johnny
@ Johnny Ich hatte noch keine Gelegenheit, es selbst zu versuchen, aber die unformatierten Datenbankverbindungsbibliotheken sind wahrscheinlich genug. Zum Beispiel hat psycopg2 Cursor , die Rückkehr namedtupleund dictdirekt: initd.org/psycopg/docs/extras.html .
jpmc26

Antworten:

309

Hast du es versucht:

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

oder:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names
Miguel
quelle
7
Wie schreiben Sie die Transaktion fest, wenn Sie eine Einfügung oder Aktualisierung vornehmen?
David S
14
Wenn Sie Raw-SQL verwenden, steuern Sie die Transaktionen, sodass Sie die Anweisungen BEGINund COMMITselbst selbst ausgeben müssen .
Miguel
1
Funktionieren dieselben SQL-Befehle, wenn Sie sie ohne SQLAlchemy ausgeben? Möglicherweise möchten Sie das Debuggen in Ihrer Datenbank aktivieren, damit Sie sehen können, welche Befehle ausgeführt werden.
Miguel
27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))führt es aus und legt es auch fest.
Devi
8
@Miguel "Wenn Sie Raw SQL verwenden, steuern Sie die Transaktionen, sodass Sie die Anweisungen BEGIN und COMMIT selbst ausgeben müssen." Das ist einfach nicht wahr. Sie können Raw SQL mit einem Sitzungsobjekt verwenden. Ich habe diesen Kommentar gerade bemerkt, aber Sie können meine Antwort für die Verwendung einer Sitzung mit Raw SQL sehen.
jpmc26
180

SQL Alchemy-Sitzungsobjekte haben ihre eigene executeMethode:

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

Alle Ihre Anwendungsabfragen sollten ein Sitzungsobjekt durchlaufen, unabhängig davon, ob es sich um unformatiertes SQL handelt oder nicht. Dadurch wird sichergestellt, dass die Abfragen ordnungsgemäß von einer Transaktion verwaltet werden , sodass mehrere Abfragen in derselben Anforderung als eine Einheit festgeschrieben oder zurückgesetzt werden können. Wenn Sie die Transaktion über die Engine oder die Verbindung verlassen , besteht ein viel höheres Risiko für subtile, möglicherweise schwer zu erkennende Fehler, die zu beschädigten Daten führen können. Jede Anforderung sollte nur einer Transaktion zugeordnet sein. Durch die Verwendung db.sessionwird sichergestellt, dass dies für Ihre Anwendung der Fall ist.

Beachten Sie auch, dass dies executefür parametrisierte Abfragen ausgelegt ist . Verwenden Sie Parameter wie :valim Beispiel für alle Eingaben in die Abfrage, um sich vor SQL-Injection-Angriffen zu schützen. Sie können den Wert für diese Parameter angeben, indem Sie a dictals zweites Argument übergeben, wobei jeder Schlüssel der Name des Parameters ist, wie er in der Abfrage angezeigt wird. Die genaue Syntax des Parameters selbst kann je nach Datenbank unterschiedlich sein, aber alle wichtigen relationalen Datenbanken unterstützen sie in irgendeiner Form.

Angenommen, es handelt sich um eine SELECTAbfrage, wird eine iterierbare Anzahl von RowProxyObjekten zurückgegeben.

Sie können mit verschiedenen Techniken auf einzelne Spalten zugreifen:

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

Persönlich ziehe ich es vor, die Ergebnisse in namedtuples umzuwandeln :

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)

Wenn Sie die Flask-SQLAlchemy-Erweiterung nicht verwenden, können Sie dennoch problemlos eine Sitzung verwenden:

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})
jpmc26
quelle
Eine Auswahl gibt einen ResultProxy zurück.
Alan B
@ AlanB Ja. Ich habe meine Worte schlecht gewählt, als ich sie eine Sequenz nannte, was bedeutet, dass sie das Sequenzprotokoll implementiert. Ich habe korrigiert und geklärt. Vielen Dank.
jpmc26
@ jpmc26 sollte die Sitzung schließen, nachdem die Abfrage wie db.session.close () ausgeführt wurde? Und wird es weiterhin die Vorteile des Verbindungspoolings haben?
Ravi Malhotra
58

docs: SQL Expression Language Tutorial - Verwenden von Text

Beispiel:

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')
Jake Berger
quelle
1
Der Link zu den sqlalchemy-Dokumenten scheint veraltet zu sein. Dies ist neuer: docs.sqlalchemy.org/en/latest/core/…
Carl
1
Darf ich fragen, warum wir verwenden ==?
Nam G VU
1
@ Jake Berger ein großes Dankeschön für dich. Ich habe fast einen Tag auf der Suche nach dieser Antwort verschwendet. Ich habe gerade die SQL direkt ausgeführt, ohne sie in Text umzuwandeln. Es war ein Fehler, wenn wir% Studenten% in meiner where-Klausel haben. Ein großer Applaus für Ihre Antwort.
Suresh Kumar
1
@NamGVU, da es wie in den meisten Programmiersprachen =normalerweise für die Zuweisung eines Werts reserviert ist. während ==für den Vergleich von Werten reserviert ist
Jake Berger
2
@ JakeBerger Hast du einen Link dafür? SQL ist keine solche Sprache, und nach den SQLAlchemy-Dokumenten ist dies nicht der Fall .
Johndodo
36

Sie können die Ergebnisse von SELECT SQL-Abfragen mit from_statement()und text()wie hier gezeigt abrufen . Auf diese Weise müssen Sie nicht mit Tupeln umgehen. Als Beispiel für eine Klasse Usermit dem Tabellennamen userskönnen Sie Folgendes versuchen:

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

return user
TrigonaMinima
quelle
15
result = db.engine.execute(text("<sql here>"))

führt das aus <sql here>, schreibt es jedoch nur fest, wenn Sie sich im autocommitModus befinden. Einfügungen und Aktualisierungen werden also nicht in der Datenbank angezeigt.

Um sich nach den Änderungen zu verpflichten, tun Sie dies

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

Dies ist eine vereinfachte Antwort zum Ausführen einer SQL-Abfrage über Flask Shell

Ordnen Sie zunächst Ihr Modul zu (wenn sich Ihr Modul / Ihre App im Hauptordner "manage.py" befindet und Sie sich in einem UNIX-Betriebssystem befinden), führen Sie Folgendes aus:

export FLASK_APP=manage

Führen Sie die Kolbenschale aus

flask shell

Importieren Sie, was wir brauchen ::

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

Führen Sie Ihre Abfrage aus:

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

Dies verwendet die aktuelle Datenbankverbindung, die die Anwendung hat.

Luigi Lopez
quelle
0

Haben Sie versucht, connection.execute(text( <sql here> ), <bind params here> )Parameter wie in den Dokumenten beschrieben zu verwenden und zu binden ? Dies kann helfen, viele Parameterformatierungs- und Leistungsprobleme zu lösen. Vielleicht ist der Gateway-Fehler eine Zeitüberschreitung? Bindungsparameter führen dazu, dass komplexe Abfragen wesentlich schneller ausgeführt werden.

jhnwsk
quelle
2
laut docs sollte es sein connection.execute(text(<sql here>), <bind params> ). bind paramssollte NICHT in sein text(). Eingabe der Bindeparameter in die Methode execute ()
Jake Berger
Jakes Verbindung ist unterbrochen. Ich denke, dies ist die URL, die jetzt relevant ist: docs.sqlalchemy.org/en/latest/core/…
code_dredd
-1

Wenn Sie Tupeln vermeiden wollen, ist eine andere Art und Weise durch den Aufruf der first, oneoder allMethoden:

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

assert query.first().name == "Welcome to my blog"
Joe Gasewicz
quelle