Come posso vedere le query SQL non elaborate che Django è in esecuzione?


307

C'è un modo per mostrare l'SQL che Django è in esecuzione durante l'esecuzione di una query?

Risposte:


372

Consulta le domande frequenti sui documenti: " Come posso vedere le query SQL non elaborate che Django è in esecuzione? "

django.db.connection.queries contiene un elenco delle query SQL:

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

Le query hanno anche un queryattributo contenente la query da eseguire:

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

Si noti che l'output della query non è SQL valido, poiché:

"Django non interpola mai effettivamente i parametri: invia la query e i parametri separatamente all'adattatore del database, che esegue le operazioni appropriate."

Dalla segnalazione bug # 17741 di Django .

Per questo motivo, non è necessario inviare l'output della query direttamente a un database.


13
A prova di questa risposta, dovresti piuttosto collegare la versione corrente della documentazione di Django: docs.djangoproject.com/en/dev/faq/models/…
Andre Miller,

5
Bella risposta. Tuttavia, si consiglia di utilizzare la str()funzione Pythonian incorporata specificata , che richiama il __str__()metodo interno . ad esempio str(MyModel.objects.filter(name="my name").query) , consiglierei anche di usare IPython e la shell Django del tuo progetto. Il completamento della scheda fornisce quindi l'introspezione degli oggetti. Poiché Django è noto per i suoi assertivi schemi di denominazione, questa metodologia tende ad essere molto utile.
Lorenz Lo Sauer,

7
Si noti che l'output di querySQL non è valido perché "Django non interpola mai effettivamente i parametri: invia la query e i parametri separatamente all'adattatore del database, che esegue le operazioni appropriate." Fonte: code.djangoproject.com/ticket/17741
gregoltsov

3
@AndreMiller Dovresti usare stable, non dev, per collegarti alla versione corrente di Django, in questo modo: docs.djangoproject.com/en/stable/faq/models/…
Flimm

3
django.db.connection.queries restituisce un elenco vuoto
fantastory

61

Django-extensions ha un comando shell_plus con un parametroprint-sql

./manage.py shell_plus --print-sql

In django-shell verranno stampate tutte le query eseguite

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>

1
Lo sto usando con --print-sql o con SHELL_PLUS_PRINT_SQL = True e non aiuta - non riesco ancora a vedere le query. qualche idea del perché? Django 1.8
Dejell,

1
Devi impostare DEBUG = True in settings.py per vedere le query
Konstantin Voschanov,

50

Dai un'occhiata a debug_toolbar , è molto utile per il debug.

La documentazione e la fonte sono disponibili su http://django-debug-toolbar.readthedocs.io/ .

Schermata della barra degli strumenti di debug


1
debug_toolbar è particolarmente utile quando hai una query che fallisce con un errore di sintassi SQL; visualizzerà l'ultima query che ha tentato di essere eseguita (e non è riuscita), semplificando il debug.
scoopseven,

L'unica cosa è che vedi query SQL sul browser. Se si eseguono test dal terminale e si desidera vederlo lì, questa non è una soluzione praticabile. Comunque grande, lo sto usando fino ad oggi.
Erdin Eray,

24
q = Query.objects.values('val1','val2','val_etc')

print q.query

risposta molto semplice! Nizza
Espoir Murhabazi,

Questa funzionalità è stata rimossa? Non funziona quando lo faccio m = MyModel.objects.get(...)seguito dam.query
sg

Questo perché mnon è più un queryset. Usa q = MyModel.objects.filter(...), quindi q.query, quindi m = q.get().
Brouwer,

24

Nessun'altra risposta copre questo metodo, quindi:

Trovo di gran lunga il metodo più utile, semplice e affidabile è chiedere al tuo database. Ad esempio su Linux per Postgres potresti fare:

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

Ogni database avrà una procedura leggermente diversa. Nei registri del database vedrai non solo l'SQL non elaborato, ma qualsiasi configurazione di connessione o overj della transazione posizionata sul sistema.


8
non dimenticate di impostare log_statement='all'in postgresql.confper questo metodo.
Ricky:

2
Puoi trovarlo postgresql.confcorrendopsql -U postgres -c 'SHOW config_file'
kramer65

17

Sebbene sia possibile farlo con il codice fornito, trovo che l'uso dell'app della barra degli strumenti di debug sia un ottimo strumento per mostrare le query. Puoi scaricarlo da github qui .

Ciò ti dà la possibilità di mostrare tutte le query eseguite su una determinata pagina insieme al tempo impiegato per la query. Riassume anche il numero di query su una pagina insieme al tempo totale per una rapida revisione. Questo è un ottimo strumento, quando vuoi vedere cosa fa il Django ORM dietro le quinte. Ha anche molte altre belle funzioni che puoi usare se vuoi.


2
Mi sembra che questa sia la versione migliore: github.com/django-debug-toolbar/django-debug-toolbar
philfreo,

15

Un'altra opzione, vedi le opzioni di registrazione in settings.py descritte da questo post

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

debug_toolbar rallenta il caricamento di ogni pagina sul server di sviluppo, la registrazione non è più veloce. Gli output possono essere scaricati su console o file, quindi l'interfaccia utente non è così piacevole. Ma per le viste con molti SQL, il debug e l'ottimizzazione degli SQL tramite debug_toolbar possono richiedere molto tempo poiché ogni caricamento della pagina è così lento.


Eccellente! Mentre la barra degli strumenti sembra grande, penso che questa risposta dovrebbe essere quella accettata. Questa è la soluzione che volevo perché consente a "manage.py RunServer" di registrare SQL sulla console e funziona con "manage.py migrate". Quest'ultimo mi ha fatto vedere che "on delete cascade" non era assolutamente impostato quando sono state create le mie tabelle. Vale la pena notare che questa risposta si basa su docs.djangoproject.com/en/1.9/topics/logging/…
LS

10

Se ti assicuri che il tuo file settings.py abbia:

  1. django.core.context_processors.debug elencato in CONTEXT_PROCESSORS
  2. DEBUG=True
  3. sei IPnella INTERNAL_IPStupla

Quindi dovresti avere accesso alla sql_queriesvariabile. Aggiungo un piè di pagina a ciascuna pagina simile a questa:

{%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 %}

Ho ottenuto la variabile sql_time_sumaggiungendo la linea

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

alla funzione di debug in django_src / django / core / context_processors.py.


1
Ho appena provato questo e (dopo aver rimosso la parte sql_time_sum), ho ottenuto: Nessun ciclo con nome nel modello. "dispari, pari" non è definito: cosa mi sto perdendo?
naufrago il

8

Ho sviluppato un'estensione per questo scopo, così puoi facilmente mettere un decoratore sulla tua funzione di visualizzazione e vedere quante query vengono eseguite.

Installare:

$ pip install django-print-sql

Per utilizzare come gestore del contesto:

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()

Per usare come decoratore:

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


3

Credo che dovrebbe funzionare se stai usando PostgreSQL:

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())}')

Questo ha funzionato anche in Python 2. Solo un refactor come print (cursor.mogrify (* qs.query.sql_with_params ())) è tutto ciò di cui ha bisogno.
iChux,

IIRC Cursor.mogrify restituisce una stringa, quindi suppongo che l'uso della stringa f per la formattazione sia superfluo ..
chander

2

Di seguito restituisce la query come SQL valido, basato su 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 '):]

2

Ho creato un piccolo frammento che puoi usare:

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)

Ci vuole come funzione parametri (contiene query sql) per ispezionare e args, kwargs necessari per chiamare quella funzione. Di conseguenza restituisce quale funzione restituisce e stampa le query SQL in una console.


1

Ho inserito questa funzione in un file util in una delle app del mio progetto:

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)))

Quindi, quando necessario, lo importa e lo chiamo da qualsiasi contesto (di solito una vista) è necessario, ad esempio:

# ... 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

È bello farlo al di fuori del modello perché se hai viste API (di solito Django Rest Framework), è applicabile anche lì.


1

Per Django 2.2:

Poiché la maggior parte delle risposte non mi ha aiutato molto durante l'utilizzo ./manage.py shell. Finalmente ho trovato la risposta. Spero che questo aiuti a qualcuno.

Per visualizzare tutte le query:

from django.db import connection
connection.queries

Per visualizzare la query per una singola query:

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

q.querysto solo mostrando l'oggetto per me. Utilizzando __str__()(Rappresentazione stringa) è stata visualizzata la query completa.


0

Visualizza le query utilizzando django.db.connection.queries

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

Accedi alla query SQL non elaborata sull'oggetto QuerySet

 qs = MyModel.objects.all()
 print(qs.query)

0

Solo per aggiungere, in django, se hai una query come:

MyModel.objects.all()

fare:

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

per ottenere la stringa sql

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.