Wie kann ich die unformatierten SQL-Abfragen sehen, die Django ausführt?

307

Gibt es eine Möglichkeit, dem SQL anzuzeigen, dass Django ausgeführt wird, während eine Abfrage ausgeführt wird?

spence91
quelle

Antworten:

372

Siehe die häufig gestellten Fragen zu Dokumenten: " Wie kann ich die unformatierten SQL-Abfragen sehen, die Django ausführt? "

django.db.connection.queries enthält eine Liste der SQL-Abfragen:

from django.db import connection
print(connection.queries)

Abfragesätze haben auch ein queryAttribut, das die auszuführende Abfrage enthält:

print(MyModel.objects.filter(name="my name").query)

Beachten Sie, dass die Ausgabe der Abfrage kein gültiges SQL ist, weil:

"Django interpoliert die Parameter nie wirklich: Es sendet die Abfrage und die Parameter separat an den Datenbankadapter, der die entsprechenden Operationen ausführt."

Aus dem Django-Fehlerbericht # 17741 .

Aus diesem Grund sollten Sie keine Abfrageausgabe direkt an eine Datenbank senden.

geowa4
quelle
13
Um diese Antwort zukunftssicher zu machen, sollten Sie lieber die aktuelle Version der Dokumentation von Django verlinken
Andre Miller
5
Gute Antwort. Es wird jedoch empfohlen, die angegebene integrierte Pythonian- str()Funktion zu verwenden, die die interne __str__()Methode aufruft . zB str(MyModel.objects.filter(name="my name").query) würde ich auch mit IPython und die Django - Shell Ihres Projekts empfehlen. Die Tab-Vervollständigung bietet dann eine Selbstbeobachtung des Objekts. Da Django für seine durchsetzungsfähigen Namensschemata bekannt ist, ist diese Methode in der Regel sehr nützlich.
Lorenz Lo Sauer
7
Beachten Sie, dass die Ausgabe von querySQL nicht gültig ist, da "Django die Parameter nie tatsächlich interpoliert: Es sendet die Abfrage und die Parameter separat an den Datenbankadapter, der die entsprechenden Operationen ausführt." Quelle: code.djangoproject.com/ticket/17741
gregoltsov
3
@AndreMiller Sie sollten stablenicht verwenden dev, um auf die aktuelle Version von Django zu verlinken, wie folgt
Flimm
3
django.db.connection.queries gibt leere Liste zurück
fantastisch
61

Django-Erweiterungen haben einen Befehl shell_plus mit einem Parameterprint-sql

./manage.py shell_plus --print-sql

In der Django-Shell werden alle ausgeführten Abfragen gedruckt

Ex.:

User.objects.get(pk=1)
SELECT "auth_user"."id",
       "auth_user"."password",
       "auth_user"."last_login",
       "auth_user"."is_superuser",
       "auth_user"."username",
       "auth_user"."first_name",
       "auth_user"."last_name",
       "auth_user"."email",
       "auth_user"."is_staff",
       "auth_user"."is_active",
       "auth_user"."date_joined"
FROM "auth_user"
WHERE "auth_user"."id" = 1

Execution time: 0.002466s [Database: default]

<User: username>
Patrick Z.
quelle
1
Ich benutze es mit --print-sql oder mit SHELL_PLUS_PRINT_SQL = True und es hilft nicht - ich kann die Abfragen immer noch nicht sehen. Irgendeine Idee warum? Django 1.8
Dejell
1
Sie müssen DEBUG = True in Ihrer settings.py setzen, um Abfragen zu sehen
Konstantin Voschanov
50

Schauen Sie sich debug_toolbar an , es ist sehr nützlich für das Debuggen.

Dokumentation und Quelle finden Sie unter http://django-debug-toolbar.readthedocs.io/ .

Screenshot der Debug-Symbolleiste

Glader
quelle
1
debug_toolbar ist besonders nützlich, wenn Sie eine Abfrage haben, die mit einem SQL-Syntaxfehler fehlschlägt. Es wird die letzte Abfrage angezeigt, die versucht wurde (und fehlgeschlagen ist), was das Debuggen erleichtert.
Scoopseven
Das einzige, was Sie sehen, sind SQL-Abfragen im Browser. Wenn Sie Tests vom Terminal aus ausführen und diese dort anzeigen möchten, ist dies keine praktikable Lösung. Trotzdem großartig, ich habe es bis heute benutzt.
Erdin Eray
24
q = Query.objects.values('val1','val2','val_etc')

print q.query
jgabrielsk8
quelle
sehr einfache Antwort! Nizza
Espoir Murhabazi
Wurde diese Funktionalität entfernt? Es funktioniert nicht, wenn ich m = MyModel.objects.get(...)gefolgt vonm.query
sg
Das liegt daran, dass mes sich nicht mehr um ein Abfrageset handelt. Verwenden Sie q = MyModel.objects.filter(...)dann q.query, dann m = q.get().
Brouwer
24

Keine andere Antwort behandelt diese Methode, also:

Ich finde die mit Abstand nützlichste, einfachste und zuverlässigste Methode, Ihre Datenbank zu fragen. Unter Linux für Postgres können Sie beispielsweise Folgendes tun:

sudo su postgres
tail -f /var/log/postgresql/postgresql-8.4-main.log

Jede Datenbank hat ein etwas anderes Verfahren. In den Datenbankprotokollen sehen Sie nicht nur das unformatierte SQL, sondern auch alle Verbindungsaufbau- oder Transaktionskosten, die Django auf dem System platziert.

Bryce
quelle
8
vergessen Sie nicht , setzen log_statement='all'in postgresql.confdiesem Verfahren.
RickyA
2
Sie können Ihre finden, postgresql.confindem Siepsql -U postgres -c 'SHOW config_file'
kramer65
17

Obwohl Sie dies mit dem bereitgestellten Code tun können, finde ich, dass die Verwendung der Debug-Symbolleisten-App ein großartiges Werkzeug ist, um Abfragen anzuzeigen. Sie können es herunterladen von GitHub hier .

Auf diese Weise können Sie alle auf einer bestimmten Seite ausgeführten Abfragen zusammen mit der Zeit anzeigen, die für die Abfrage benötigt wurde. Außerdem wird die Anzahl der Abfragen auf einer Seite zusammen mit der Gesamtzeit für eine schnelle Überprüfung zusammengefasst. Dies ist ein großartiges Tool, wenn Sie einen Blick hinter die Kulissen des Django ORM werfen möchten. Es hat auch viele andere nette Funktionen, die Sie verwenden können, wenn Sie möchten.

googletorp
quelle
2
Sieht für mich so aus, als wäre dies die beste Version: github.com/django-debug-toolbar/django-debug-toolbar
philfreo
15

Eine weitere Option finden Sie unter Protokollierungsoptionen in settings.py, die in diesem Beitrag beschrieben werden

http://dabapps.com/blog/logging-sql-queries-django-13/

debug_toolbar verlangsamt das Laden jeder Seite auf Ihrem Entwicklungsserver. Die Protokollierung erfolgt nicht, sodass sie schneller ist. Die Ausgaben können in eine Konsole oder eine Datei geschrieben werden, sodass die Benutzeroberfläche nicht so gut ist. Bei Ansichten mit vielen SQLs kann das Debuggen und Optimieren der SQLs über debug_toolbar jedoch lange dauern, da das Laden jeder Seite so langsam ist.

Übertaktet
quelle
Ausgezeichnet! Die Symbolleiste sieht zwar gut aus, aber ich denke, diese Antwort sollte akzeptiert werden. Dies ist die Lösung, die ich wollte, da "manage.py runserver" SQL in der Konsole protokollieren kann und mit "manage.py migrate" funktioniert. Letzteres ließ mich sehen, dass "On Delete Cascade" definitiv nicht festgelegt wurde, als meine Tabellen erstellt wurden. Es ist erwähnenswert, dass diese Antwort auf docs.djangoproject.com/de/1.9/topics/logging/…
LS
10

Wenn Sie sicherstellen, dass Ihre Datei settings.py Folgendes enthält:

  1. django.core.context_processors.debug aufgelistet in CONTEXT_PROCESSORS
  2. DEBUG=True
  3. Du bist IPim INTERNAL_IPSTupel

Dann sollten Sie Zugriff auf die sql_queriesVariable haben. Ich füge jeder Seite, die so aussieht, eine Fußzeile hinzu:

{%if sql_queries %}
  <div class="footNav">
    <h2>Queries</h2>
    <p>
      {{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }}, {{sql_time_sum}} Time
    {% ifnotequal sql_queries|length 0 %}
      (<span style="cursor: pointer;" onclick="var s=document.getElementById('debugQueryTable').style;s.disp\
lay=s.display=='none'?'':'none';this.innerHTML=this.innerHTML=='Show'?'Hide':'Show';">Show</span>)
    {% endifnotequal %}
    </p>
    <table id="debugQueryTable" style="display: none;">
      <col width="1"></col>
      <col></col>
      <col width="1"></col>
      <thead>
        <tr>
          <th scope="col">#</th>
          <th scope="col">SQL</th>
          <th scope="col">Time</th>
        </tr>
      </thead>
      <tbody>
        {% for query in sql_queries %}
          <tr class="{% cycle odd,even %}">
            <td>{{ forloop.counter }}</td>
            <td>{{ query.sql|escape }}</td>
            <td>{{ query.time }}</td>
          </tr>
        {% endfor %}
      </tbody>
    </table>
  </div>
{% endif %}

Ich habe die Variable sql_time_sumdurch Hinzufügen der Zeile erhalten

context_extras['sql_time_sum'] = sum([float(q['time']) for q in connection.queries])

zur Debug-Funktion in django_src / django / core / context_processors.py.

Mike Howsden
quelle
1
Ich habe es gerade versucht und (nachdem ich den Teil sql_time_sum entfernt habe) erhalten: Keine benannten Zyklen in der Vorlage. 'ungerade, gerade' ist nicht definiert - was fehlt mir?
Castaway
8

Zu diesem Zweck habe ich eine Erweiterung entwickelt, mit der Sie Ihre Ansichtsfunktion einfach mit einem Dekorator versehen und sehen können, wie viele Abfragen ausgeführt werden.

Installieren:

$ pip install django-print-sql

So verwenden Sie als Kontextmanager:

from django_print_sql import print_sql

# set `count_only` to `True` will print the number of executed SQL statements only
with print_sql(count_only=False):

  # write the code you want to analyze in here,
  # e.g. some complex foreign key lookup,
  # or analyzing a DRF serializer's performance

  for user in User.objects.all()[:10]:
      user.groups.first()

Als Dekorateur verwenden:

from django_print_sql import print_sql_decorator


@print_sql_decorator(count_only=False)  # this works on class-based views as well
def get(request):
    # your view code here

Github: https://github.com/rabbit-aaron/django-print-sql

rabbit.aaron
quelle
3

Ich glaube, das sollte funktionieren, wenn Sie PostgreSQL verwenden:

from django.db import connections
from app_name import models
from django.utils import timezone

# Generate a queryset, use your favorite filter, QS objects, and whatnot.
qs=models.ThisDataModel.objects.filter(user='bob',date__lte=timezone.now())

# Get a cursor tied to the default database
cursor=connections['default'].cursor()

# Get the query SQL and parameters to be passed into psycopg2, then pass
# those into mogrify to get the query that would have been sent to the backend
# and print it out. Note F-strings require python 3.6 or later.
print(f'{cursor.mogrify(*qs.query.sql_with_params())}')
chander
quelle
Dies funktionierte sogar in Python 2. Nur ein Refactor wie print (cursor.mogrify (* qs.query.sql_with_params ())) ist alles, was es braucht.
iChux
IIRC Cursor.mogrify gibt eine Zeichenfolge zurück, daher ist die Verwendung der Zeichenfolge f für die Formatierung
vermutlich
2

Im Folgenden wird die Abfrage als gültiges SQL basierend auf https://code.djangoproject.com/ticket/17741 zurückgegeben :

def str_query(qs):
    """
    qs.query returns something that isn't valid SQL, this returns the actual
    valid SQL that's executed: https://code.djangoproject.com/ticket/17741
    """
    cursor = connections[qs.db].cursor()
    query, params = qs.query.sql_with_params()
    cursor.execute('EXPLAIN ' + query, params)
    res = str(cursor.db.ops.last_executed_query(cursor, query, params))
    assert res.startswith('EXPLAIN ')
    return res[len('EXPLAIN '):]
Blitz
quelle
2

Ich habe einen kleinen Ausschnitt erstellt, den Sie verwenden können:

from django.conf import settings
from django.db import connection


def sql_echo(method, *args, **kwargs):
    settings.DEBUG = True
    result = method(*args, **kwargs)
    for query in connection.queries:
        print(query)
    return result


# HOW TO USE EXAMPLE:
# 
# result = sql_echo(my_method, 'whatever', show=True)

Es wird als Parameterfunktion (enthält SQL-Abfragen) zum Überprüfen und Argumentieren verwendet, kwargs, die zum Aufrufen dieser Funktion benötigt werden. Als Ergebnis wird zurückgegeben, welche Funktion zurückgegeben wird, und SQL-Abfragen werden in einer Konsole gedruckt.

türkus
quelle
1

Ich habe diese Funktion in eine Util-Datei in einer der Apps in meinem Projekt eingefügt:

import logging
import re

from django.db import connection

logger = logging.getLogger(__name__)

def sql_logger():
    logger.debug('TOTAL QUERIES: ' + str(len(connection.queries)))
    logger.debug('TOTAL TIME: ' + str(sum([float(q['time']) for q in connection.queries])))

    logger.debug('INDIVIDUAL QUERIES:')
    for i, query in enumerate(connection.queries):
        sql = re.split(r'(SELECT|FROM|WHERE|GROUP BY|ORDER BY|INNER JOIN|LIMIT)', query['sql'])
        if not sql[0]: sql = sql[1:]
        sql = [(' ' if i % 2 else '') + x for i, x in enumerate(sql)]
        logger.debug('\n### {} ({} seconds)\n\n{};\n'.format(i, query['time'], '\n'.join(sql)))

Dann importiere ich es bei Bedarf einfach und rufe es aus einem beliebigen Kontext (normalerweise einer Ansicht) auf, z. B.:

# ... other imports
from .utils import sql_logger

class IngredientListApiView(generics.ListAPIView):
    # ... class variables and such

    # Main function that gets called when view is accessed
    def list(self, request, *args, **kwargs):
        response = super(IngredientListApiView, self).list(request, *args, **kwargs)

        # Call our function
        sql_logger()

        return response

Es ist schön, dies außerhalb der Vorlage zu tun, denn wenn Sie API-Ansichten haben (normalerweise Django Rest Framework), ist dies auch dort anwendbar.

getup8
quelle
1

Für Django 2.2:

Da mir die meisten Antworten bei der Verwendung nicht viel geholfen haben ./manage.py shell . Endlich habe ich die Antwort gefunden. Hoffe das hilft jemandem.

So zeigen Sie alle Abfragen an:

from django.db import connection
connection.queries

So zeigen Sie die Abfrage für eine einzelne Abfrage an:

q=Query.objects.all()
q.query.__str__()

q.queryIch zeige nur das Objekt für mich. Bei Verwendung der __str__()(String-Darstellung) wird die vollständige Abfrage angezeigt.

goutham_mi3
quelle
0

Anzeigen von Abfragen mit django.db.connection.queries

from django.db import connection
print(connection.queries)

Greifen Sie auf eine unformatierte SQL-Abfrage für das QuerySet-Objekt zu

 qs = MyModel.objects.all()
 print(qs.query)
Muhammad Parwej
quelle
0

Nur um in Django hinzuzufügen, wenn Sie eine Frage haben wie:

MyModel.objects.all()

tun:

MyModel.objects.all().query.sql_with_params()

um die SQL-Zeichenfolge zu erhalten

Robert Wallace
quelle