¿Cómo puedo ver las consultas SQL sin procesar que Django está ejecutando?

308

¿Hay alguna manera de mostrar el SQL que Django está ejecutando mientras realiza una consulta?

spence91
fuente

Respuestas:

373

Consulte las preguntas frecuentes sobre documentos: " ¿Cómo puedo ver las consultas SQL sin procesar que Django está ejecutando? "

django.db.connection.queries contiene una lista de las consultas SQL:

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

Los conjuntos de consultas también tienen un queryatributo que contiene la consulta a ejecutar:

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

Tenga en cuenta que el resultado de la consulta no es SQL válido, porque:

"Django nunca interpola los parámetros: envía la consulta y los parámetros por separado al adaptador de la base de datos, que realiza las operaciones apropiadas".

Del informe de error Django # 17741 .

Debido a eso, no debe enviar el resultado de la consulta directamente a una base de datos.

geowa4
fuente
13
Para probar esta respuesta en el futuro, debe vincular la versión actual de la documentación de Django: docs.djangoproject.com/en/dev/faq/models/…
Andre Miller el
55
Gran respuesta. Sin embargo, se recomienda utilizar la str()función Pythonian incorporada especificada , que invoca el __str__()método interno . por ejemplo str(MyModel.objects.filter(name="my name").query) , también recomendaría usar IPython y el shell Django de su proyecto. La finalización de la pestaña proporciona introspección de objetos Como Django es conocido por sus esquemas de nombres asertivos, esta metodología tiende a ser muy útil.
Lorenz Lo Sauer
77
Tenga en cuenta que la salida de querySQL no es válida, porque "Django nunca interpola los parámetros: envía la consulta y los parámetros por separado al adaptador de la base de datos, que realiza las operaciones apropiadas". Fuente: code.djangoproject.com/ticket/17741
gregoltsov
3
@AndreMiller Debe usar stable, no dev, para vincular a la versión actual de Django, de esta manera: docs.djangoproject.com/en/stable/faq/models/…
Flimm
3
django.db.connection.queries devuelve una lista vacía
fantastory
61

Las extensiones Django tienen un comando shell_plus con un parámetroprint-sql

./manage.py shell_plus --print-sql

En django-shell se imprimirán todas las consultas ejecutadas

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
fuente
1
Lo estoy usando con --print-sql o con SHELL_PLUS_PRINT_SQL = True y no ayuda, todavía no puedo ver las consultas. alguna idea de por qué? django 1.8
Dejell
1
Debe
50

Eche un vistazo a debug_toolbar , es muy útil para la depuración.

La documentación y la fuente están disponibles en http://django-debug-toolbar.readthedocs.io/ .

Captura de pantalla de la barra de herramientas de depuración

Glader
fuente
1
debug_toolbar es especialmente útil cuando tiene una consulta que falla con un error de sintaxis SQL; mostrará la última consulta que intentó ejecutarse (y falló), lo que facilita la depuración.
scoopseven
Lo único es que ves consultas SQL en el navegador. Si ejecuta pruebas desde la terminal y desea verlo allí, esta no es una solución viable. Todavía genial, lo he estado usando hasta el día de hoy.
Erdin Eray
24
q = Query.objects.values('val1','val2','val_etc')

print q.query
jgabrielsk8
fuente
respuesta muy simple! Nice
Espoir Murhabazi
¿Se ha eliminado esta funcionalidad? No funciona cuando lo hago m = MyModel.objects.get(...)seguido porm.query
sg
Eso es porque mya no es un conjunto de consultas. Uso q = MyModel.objects.filter(...), entonces q.query, entonces m = q.get().
Brouwer
24

Ninguna otra respuesta cubre este método, entonces:

Creo que, con mucho, el método más útil, simple y confiable es preguntarle a su base de datos. Por ejemplo, en Linux para Postgres, puede hacer:

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

Cada base de datos tendrá un procedimiento ligeramente diferente. En los registros de la base de datos, verá no solo el SQL sin procesar, sino también cualquier configuración de conexión o sobrecarga de transacciones que django está colocando en el sistema.

Bryce
fuente
8
no se olvide de establecer log_statement='all'en postgresql.confeste método.
RickyA
2
Puedes encontrar tu postgresql.confcorriendopsql -U postgres -c 'SHOW config_file'
kramer65
17

Aunque puede hacerlo con el código suministrado, encuentro que usar la aplicación de la barra de herramientas de depuración es una gran herramienta para mostrar consultas. Puedes descargarlo desde github aquí .

Esto le da la opción de mostrar todas las consultas ejecutadas en una página determinada junto con el tiempo de consulta que tomó. También resume el número de consultas en una página junto con el tiempo total para una revisión rápida. Esta es una gran herramienta, cuando quieres ver lo que hace el Django ORM detrás de escena. También tiene muchas otras características agradables, que puede usar si lo desea.

googletorp
fuente
2
Me parece que esta es la mejor versión: github.com/django-debug-toolbar/django-debug-toolbar
philfreo
15

Otra opción, vea las opciones de registro en settings.py descritas en esta publicación

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

debug_toolbar ralentiza cada carga de página en su servidor de desarrollo, el registro no lo hace así que es más rápido. Las salidas se pueden volcar a la consola o al archivo, por lo que la interfaz de usuario no es tan agradable. Pero para las vistas con muchos SQL, puede llevar mucho tiempo depurar y optimizar los SQL a través de debug_toolbar ya que cada carga de página es muy lenta.

Overclockeado
fuente
¡Excelente! Si bien la barra de herramientas se ve genial, creo que esta respuesta debería ser la aceptada. Esta es la solución que quería porque permite que "manage.py runserver" registre SQL en la consola y funciona con "manage.py migrate". Este último me permitió ver que "al eliminar la cascada" definitivamente no se configuraba cuando se crean mis tablas. Vale la pena señalar que esta respuesta se basa en docs.djangoproject.com/en/1.9/topics/logging/…
LS
10

Si se asegura de que su archivo settings.py tenga:

  1. django.core.context_processors.debug enumerado en CONTEXT_PROCESSORS
  2. DEBUG=True
  3. estás IPen la INTERNAL_IPStupla

Entonces deberías tener acceso a la sql_queriesvariable. Añado un pie de página a cada página que se ve así:

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

Obtuve la variable sql_time_sumagregando la línea

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

a la función de depuración en django_src / django / core / context_processors.py.

Mike Howsden
fuente
1
Acabo de intentar esto, y (después de haber eliminado la parte sql_time_sum), obtuve: No hay ciclos con nombre en la plantilla. 'impar, par' no está definido: ¿qué me estoy perdiendo?
náufrago
8

Desarrollé una extensión para este propósito, para que pueda poner fácilmente un decorador en su función de vista y ver cuántas consultas se ejecutan.

Instalar:

$ pip install django-print-sql

Para usar como administrador de contexto:

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

Para usar como decorador:

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

conejo.aaron
fuente
3

Creo que esto debería funcionar si está utilizando 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())}')
cambiador
fuente
Esto funcionó incluso en Python 2. Solo un refactor como print (cursor.mogrify (* qs.query.sql_with_params ())) es todo lo que necesita.
iChux
IIRC Cursor.mogrify devuelve una cadena, por lo que supongo que el uso de la cadena f para formatear es superfluo ..
chander
2

Lo siguiente devuelve la consulta como SQL válido, basado en 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 '):]
Destello
fuente
2

He hecho un pequeño fragmento que puedes usar:

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)

Toma como función de parámetros (contiene consultas sql) para inspeccionar y args, kwargs necesarios para llamar a esa función. Como resultado, devuelve qué función devuelve e imprime consultas SQL en una consola.

turkus
fuente
1

Puse esta función en un archivo util en una de las aplicaciones de mi proyecto:

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

Luego, cuando sea necesario, solo lo importo y lo llamo desde cualquier contexto (generalmente una vista) es necesario, por ejemplo:

# ... 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 bueno hacer esto fuera de la plantilla porque si tiene vistas de API (generalmente Django Rest Framework), también es aplicable allí.

getup8
fuente
1

Para Django 2.2:

Como la mayoría de las respuestas no me ayudaron mucho al usar ./manage.py shell. Finalmente encontré la respuesta. Espero que esto ayude a alguien.

Para ver todas las consultas:

from django.db import connection
connection.queries

Para ver la consulta de una sola consulta:

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

q.querysolo mostrando el objeto para mí. El uso de __str__()(Representación de cadena) muestra la consulta completa.

goutham_mi3
fuente
0

Ver consultas usando django.db.connection.queries

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

Acceda a la consulta SQL sin procesar en el objeto QuerySet

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

Solo para agregar, en django, si tiene una consulta como:

MyModel.objects.all()

hacer:

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

para obtener la cadena sql

robert wallace
fuente