Ich möchte wirklich in der Lage sein, gültiges SQL für meine Anwendung, einschließlich Werte, auszudrucken, anstatt Parameter zu binden, aber es ist nicht offensichtlich, wie dies in SQLAlchemy gemacht wird (ich bin mir ziemlich sicher, dass dies beabsichtigt ist).
Hat jemand dieses Problem allgemein gelöst?
python
sqlalchemy
Bukzor
quelle
quelle
sqlalchemy.engine
Protokoll von SQLAlchemy tippen . Es protokolliert Abfragen und Bindungsparameter. Sie müssen lediglich die Bindungsplatzhalter durch die Werte in einer leicht zu erstellenden SQL-Abfragezeichenfolge ersetzen.Antworten:
In den allermeisten Fällen ist die "Stringifizierung" einer SQLAlchemy-Anweisung oder -Abfrage so einfach wie:
Dies gilt sowohl für ein ORM
Query
als auch für eineselect()
oder andere Aussage.Hinweis : Die folgende detaillierte Antwort wird in der sqlalchemy-Dokumentation beibehalten .
Um die Anweisung so zu kompilieren, dass sie für einen bestimmten Dialekt oder eine bestimmte Engine kompiliert wurde, können Sie diese an compile () übergeben , wenn die Anweisung selbst noch nicht an eine gebunden ist :
oder ohne Motor:
Wenn wir ein ORM-
Query
Objekt erhalten, müssencompile()
wir zuerst nur auf den .statement- Accessor zugreifen , um an die Methode zu gelangen :In Bezug auf die ursprüngliche Bestimmung, dass gebundene Parameter in die endgültige Zeichenfolge "eingebunden" werden sollen, besteht die Herausforderung darin, dass SQLAlchemy normalerweise nicht damit beauftragt ist, da dies vom Python-DBAPI angemessen behandelt wird, ganz zu schweigen von der Umgehung gebundener Parameter wahrscheinlich die am häufigsten genutzten Sicherheitslücken in modernen Webanwendungen. SQLAlchemy ist unter bestimmten Umständen, z. B. bei der Emission von DDL, nur eingeschränkt in der Lage, diese Stringifizierung durchzuführen. Um auf diese Funktionalität zuzugreifen, kann das Flag 'literal_binds' verwendet werden, das an Folgendes übergeben wird
compile_kwargs
:Der obige Ansatz hat die Einschränkungen, dass er nur für Basistypen wie Ints und Strings unterstützt wird. Wenn ein
bindparam
Wert ohne voreingestellten Wert direkt verwendet wird, kann er dies auch nicht stringifizieren.Um das Inline-Literal-Rendering für nicht unterstützte Typen zu unterstützen, implementieren Sie ein
TypeDecorator
für den Zieltyp, das eineTypeDecorator.process_literal_param
Methode enthält:Produktion produzieren wie:
quelle
query.prettyprint()
. Es lindert das Debuggen bei großen Abfragen immens.@compiles
usw.) für eine beliebige Anzahl von Paketen von Drittanbietern, um hübsche Drucksysteme zu implementieren.Dies funktioniert in Python 2 und 3 und ist etwas sauberer als zuvor, erfordert jedoch SA> = 1.0.
Demo:
Gibt diese Ausgabe: (getestet in Python 2.7 und 3.4)
quelle
Da das, was Sie möchten, nur beim Debuggen sinnvoll ist, können Sie SQLAlchemy mit starten
echo=True
, um alle SQL-Abfragen zu protokollieren. Beispielsweise:Dies kann auch nur für eine einzelne Anfrage geändert werden:
Bei Verwendung mit Flask können Sie einfach einstellen
das gleiche Verhalten zu bekommen.
quelle
flask-sqlalchemy
sollte dies die akzeptierte Antwort sein.Zu diesem Zweck können wir die Kompilierungsmethode verwenden. Aus den Dokumenten :
Ergebnis:
Warnung aus Dokumenten:
quelle
Aufbauend auf den Kommentaren von @ zzzeek zum Code von @ bukzor habe ich mir diese ausgedacht, um leicht eine "ziemlich druckbare" Abfrage zu erhalten:
Ich persönlich habe
sqlparse
Schwierigkeiten, Code zu lesen, der nicht eingerückt ist, daher habe ich SQL neu eingerückt. Es kann mit installiert werdenpip install sqlparse
.quelle
datatime.now()
bei Verwendung von Python 3 + sqlalchemy 1.0 funktionieren. Sie müssten den Ratschlägen von @ zzzeek zum Erstellen eines benutzerdefinierten TypeDecorators folgen, damit dieser auch funktioniert.Dieser Code basiert auf einer brillanten Antwort von @bukzor. Ich habe gerade ein benutzerdefiniertes Rendering für den
datetime.datetime
Typ in Oracle hinzugefügtTO_DATE()
.Fühlen Sie sich frei, den Code entsprechend Ihrer Datenbank zu aktualisieren:
quelle
return "%s" % value
stattreturn repr(value)
im Abschnitt float, int, long, weil Python Longs22L
22
"STR_TO_DATE('%s','%%Y-%%m-%%d %%H:%%M:%%S')" % value.strftime("%Y-%m-%d %H:%M:%S")
in MySQLIch möchte darauf hinweisen, dass die oben angegebenen Lösungen nicht "nur" mit nicht trivialen Abfragen funktionieren. Ein Problem, auf das ich gestoßen bin, waren kompliziertere Typen, wie z. B. pgsql ARRAYs, die Probleme verursachen. Ich habe eine Lösung gefunden, die für mich sogar mit pgsql ARRAYs funktioniert hat:
ausgeliehen von: https://gist.github.com/gsakkis/4572159
Der verknüpfte Code scheint auf einer älteren Version von SQLAlchemy zu basieren. Sie erhalten eine Fehlermeldung, dass das Attribut _mapper_zero_or_none nicht vorhanden ist. Hier ist eine aktualisierte Version, die mit einer neueren Version funktioniert. Sie ersetzen einfach _mapper_zero_or_none durch bind. Darüber hinaus werden pgsql-Arrays unterstützt:
Getestet auf zwei Ebenen verschachtelter Arrays.
quelle
from file import render_query; print(render_query(query))