Gibt es eine Möglichkeit, die SQL, die Django ausgeführt wird, während der Durchführung einer Abfrage zu zeigen?
Antworten
Zu viele Anzeigen?Wenn Sie sicherstellen, dass Ihre Datei settings.py:
django.core.context_processors.debug
aufgeführt inCONTEXT_PROCESSORS
DEBUG=True
- Ihr
IP
imINTERNAL_IPS
Tupel
Dann sollten Sie Zugriff auf die sql_queries
variabel. Ich füge an jede Seite eine Fußzeile an, die wie folgt aussieht:
{%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_sum
durch Hinzufügen der Zeile
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.
Django SQL Sniffer ist eine weitere Alternative, um rohe ausgeführte Abfragen aus jedem Prozess, der Django ORM nutzt, zu betrachten (und die Statistiken zu sehen). Ich habe es gebaut, um einen bestimmten Anwendungsfall zu befriedigen, die ich hatte, die ich nicht gesehen habe irgendwo abgedeckt, nämlich:
- keine Änderungen am Quellcode, den der Zielprozess ausführt (keine Notwendigkeit, eine neue Anwendung in den Django-Einstellungen zu registrieren, Dekoratoren überall zu importieren usw.)
- keine Änderungen an der Protokollierungskonfiguration (z. B. weil ich an einem bestimmten Prozess interessiert bin und nicht an der gesamten Prozessflotte, für die die Konfiguration gilt)
- kein Neustart des Zielprozesses erforderlich (z. B. weil es sich um eine wichtige Komponente handelt und ein Neustart zu einer gewissen Ausfallzeit führen kann)
Daher kann Django SQL Sniffer ad-hoc verwendet und an einen bereits laufenden Prozess angehängt werden. Das Tool "schnüffelt" dann in den ausgeführten Abfragen und gibt sie auf der Konsole aus, während sie ausgeführt werden. Wenn das Tool gestoppt wird, wird eine statistische Zusammenfassung mit Ausreißer-Abfragen auf der Grundlage einer möglichen Metrik (Anzahl, maximale Dauer und kombinierte Gesamtdauer) angezeigt.
Hier ist ein Screenshot eines Beispiels, bei dem ich eine Python-Shell angehängt habe
Sie können die Live-Demo und weitere Details auf der Github-Seite .
Ich habe diese Funktion in eine util-Datei in einer der Anwendungen 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)))
Bei Bedarf importiere ich sie dann einfach und rufe sie aus einem beliebigen Kontext (in der Regel 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, weil dann, wenn Sie API-Ansichten (in der Regel Django Rest Framework) haben, ist es auch dort anwendbar.
Im Folgenden wird die Abfrage als gültiges SQL zurückgegeben, basierend auf https://code.djangoproject.com/ticket/17741 :
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 '):]
Ich glaube, dies 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())}')