Como posso ver as consultas SQL brutas que o Django está executando?

307

Existe uma maneira de mostrar ao SQL que o Django está executando enquanto executa uma consulta?

spence91
fonte

Respostas:

372

Consulte a documentação da FAQ: " Como posso ver as consultas SQL brutas que o Django está executando? "

django.db.connection.queries contém uma lista das consultas SQL:

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

Os conjuntos de consultas também têm um queryatributo que contém a consulta a ser executada:

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

Observe que a saída da consulta não é SQL válido, porque:

"O Django nunca interpola realmente os parâmetros: envia a consulta e os parâmetros separadamente para o adaptador de banco de dados, que executa as operações apropriadas."

Do relatório de bug do Django # 17741 .

Por isso, você não deve enviar a saída da consulta diretamente para um banco de dados.

geowa4
fonte
13
Para prova de futuro esta resposta você deveria ligar a versão atual da documentação do Django: docs.djangoproject.com/en/dev/faq/models/...
Andre Miller
5
Ótima resposta. No entanto, é recomendável usar a str()função Pythonian incorporada especificada , que chama o __str__()método interno . Por exemplo, str(MyModel.objects.filter(name="my name").query) eu também recomendaria o uso do IPython e do shell Django do seu projeto. A conclusão da guia fornece introspecção de objetos. Como o Django é conhecido por seus esquemas de nomeação assertivos, essa metodologia tende a ser muito útil.
Lorenz Lo Sauer
7
Observe que a saída de querynão é SQL válido, porque "o Django nunca interpola realmente os parâmetros: envia a consulta e os parâmetros separadamente para o adaptador de banco de dados, que executa as operações apropriadas." Fonte: code.djangoproject.com/ticket/17741
gregoltsov
3
@AndreMiller Você deve usar stable, não dev, para link para a versão atual do Django, como este: docs.djangoproject.com/en/stable/faq/models/...
Flimm
3
django.db.connection.queries retorna uma lista vazia
fantastory
61

O Django-extensions possui um comando shell_plus com um parâmetroprint-sql

./manage.py shell_plus --print-sql

No django-shell, todas as consultas executadas serão impressas

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
fonte
1
Estou usando-o com --print-sql ou com SHELL_PLUS_PRINT_SQL = True e não ajuda - ainda não consigo ver as consultas. alguma idéia do porquê? Django 1.8
Dejell
1
Você precisa definir DEBUG = True em seu settings.py para ver as consultas
Konstantin Voschanov
50

Dê uma olhada em debug_toolbar , é muito útil para depuração.

A documentação e a fonte estão disponíveis em http://django-debug-toolbar.readthedocs.io/ .

Captura de tela da barra de ferramentas de depuração

Glader
fonte
1
debug_toolbar é especialmente útil quando você tem uma consulta que está falhando com um erro de sintaxe SQL; exibirá a última consulta que tentou executar (e falhou), facilitando a depuração.
scoopseven
A única coisa é que você vê consultas SQL no navegador. Se você executa testes no terminal e deseja vê-lo lá, esta não é uma solução viável. Ainda bem, eu tenho usado até hoje.
Erdin Eray
24
q = Query.objects.values('val1','val2','val_etc')

print q.query
jgabrielsk8
fonte
resposta muito simples! Nice
Espoir Murhabazi
Essa funcionalidade foi removida? Não funciona quando eu faço m = MyModel.objects.get(...)seguido porm.query
sg
Isso porque mnão é mais um conjunto de consultas. Use q = MyModel.objects.filter(...), então q.query, então m = q.get().
Brouwer
24

Nenhuma outra resposta cobre esse método, portanto:

Acho, de longe, o método mais útil, simples e confiável é perguntar ao seu banco de dados. Por exemplo, no Linux para Postgres, você pode fazer:

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

Cada banco de dados terá um procedimento ligeiramente diferente. Nos logs do banco de dados, você verá não apenas o SQL bruto, mas qualquer configuração de conexão ou sobrecarga de transação que o django esteja colocando no sistema.

Bryce
fonte
8
não se esqueça de definir log_statement='all'em postgresql.confpor este método.
RickyA
2
Você pode encontrar o seu postgresql.confexecutandopsql -U postgres -c 'SHOW config_file'
kramer65 1/19
17

Embora você possa fazer isso com o código fornecido, acho que o uso do aplicativo da barra de ferramentas de depuração é uma ótima ferramenta para mostrar consultas. Você pode baixá-lo no github aqui .

Isso oferece a opção de mostrar todas as consultas executadas em uma determinada página, juntamente com o tempo necessário para a consulta. Também resume o número de consultas em uma página e o tempo total para uma revisão rápida. Esta é uma ótima ferramenta, quando você deseja ver o que o Django ORM faz nos bastidores. Ele também possui muitos outros recursos interessantes que você pode usar se quiser.

googletorp
fonte
2
Parece-me que esta é a melhor versão: github.com/django-debug-toolbar/django-debug-toolbar
philfreo
15

Outra opção, consulte as opções de log em settings.py descritas nesta postagem

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

debug_toolbar diminui a velocidade de cada carregamento de página no servidor de desenvolvimento, o registro não é mais rápido. As saídas podem ser despejadas no console ou arquivo, para que a interface do usuário não seja tão boa. Porém, para visualizações com muitos SQLs, pode demorar muito tempo para depurar e otimizar os SQLs através do debug_toolbar, pois o carregamento de cada página é muito lento.

Overclock
fonte
Excelente! Embora a barra de ferramentas pareça ótima, acho que essa resposta deve ser a aceita. Esta é a solução que eu queria, porque permite que "manage.py runserver" registre o SQL no console e funcione com "manage.py migrate". O último, deixe-me ver que "em exclusão de cascata" definitivamente não estava sendo definido quando minhas tabelas foram criadas. É importante notar que esta resposta é baseada na docs.djangoproject.com/en/1.9/topics/logging/...
LS
10

Se você se certificar de que seu arquivo settings.py possui:

  1. django.core.context_processors.debug listados em CONTEXT_PROCESSORS
  2. DEBUG=True
  3. seu IPna INTERNAL_IPStupla

Então você deve ter acesso à sql_queriesvariável. Anexo um rodapé a cada página que se parece com isso:

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

Eu peguei a variável sql_time_sumadicionando a linha

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

para a função de depuração em django_src / django / core / context_processors.py.

Mike Howsden
fonte
1
Eu apenas tentei isso e (após remover a parte sql_time_sum), obtive: Nenhum ciclo nomeado no modelo. 'impar, par' não está definido - o que estou perdendo?
náufrago
8

Desenvolvi uma extensão para esse fim, para que você possa colocar facilmente um decorador na sua função de visualização e ver quantas consultas são executadas.

Para instalar:

$ pip install django-print-sql

Para usar como gerenciador 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

rabbit.aaron
fonte
3

Acredito que isso funcione se você estiver usando o 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())}')
candelabro
fonte
Isso funcionou mesmo no Python 2. Apenas um refator como print (cursor.mogrify (* qs.query.sql_with_params ())) é tudo o que precisa.
iChux 25/10/19
IIRC Cursor.mogrify retorna uma string, então eu suponho que o uso da corda f para a formatação é supérfluo ..
chander
2

A seguir, a consulta retorna como SQL válido, com base em 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 '):]
Instantâneo
fonte
2

Fiz um pequeno trecho que você pode 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)

Ele assume como função de parâmetros (contém consultas sql) para inspecionar e args, kwargs necessários para chamar essa função. Como resultado, ele retorna qual função retorna e imprime consultas SQL em um console.

turkus
fonte
1

Coloquei essa função em um arquivo util em um dos aplicativos do meu projeto:

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

Então, quando necessário, basta importá-lo e chamá-lo de qualquer contexto (geralmente uma visualização) que seja necessário, por exemplo:

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

É bom fazer isso fora do modelo, porque, se você tiver visualizações de API (geralmente o Django Rest Framework), é aplicável também.

getup8
fonte
1

Para o Django 2.2:

Como a maioria das respostas não me ajudou muito ao usar ./manage.py shell . Finalmente eu encontrei a resposta. Espero que isso ajude alguém.

Para visualizar todas as consultas:

from django.db import connection
connection.queries

Para visualizar a consulta para uma única consulta:

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

q.queryapenas exibindo o objeto para mim. O uso da __str__()(representação de String) exibiu a consulta completa.

goutham_mi3
fonte
0

Exibir consultas usando django.db.connection.queries

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

Acessar consulta SQL bruta no objeto QuerySet

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

Apenas para adicionar, no django, se você tiver uma consulta como:

MyModel.objects.all()

Faz:

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

para obter a string sql

Robert Wallace
fonte