Jak mogę zobaczyć nieprzetworzone zapytania SQL uruchomione przez Django?

307

Czy istnieje sposób pokazania kodu SQL uruchomionego przez Django podczas wykonywania zapytania?

spence91
źródło

Odpowiedzi:

372

Zobacz często zadawane pytania dotyczące dokumentacji: „ Jak mogę zobaczyć surowe zapytania SQL, które uruchamia Django?

django.db.connection.queries zawiera listę zapytań SQL:

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

Zestawy zapytań mają również queryatrybut zawierający zapytanie do wykonania:

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

Zauważ, że wynik zapytania nie jest prawidłowym SQL, ponieważ:

„Django nigdy nie interpoluje parametrów: wysyła zapytanie i parametry osobno do adaptera bazy danych, który wykonuje odpowiednie operacje.”

Z raportu o błędzie Django # 17741 .

Z tego powodu nie należy wysyłać wyników zapytania bezpośrednio do bazy danych.

geowa4
źródło
13
Aby w przyszłości potwierdzić tę odpowiedź, powinieneś raczej połączyć aktualną wersję dokumentacji Django: docs.djangoproject.com/en/dev/faq/models/…
Andre Miller
5
Świetna odpowiedź. Zalecane jest jednak użycie określonej wbudowanej str()funkcji języka Python , która wywołuje __str__()metodę wewnętrzną . np. str(MyModel.objects.filter(name="my name").query) Poleciłbym również użycie IPython i powłoki Django twojego projektu. Uzupełnianie tabulatorów zapewnia introspekcję obiektu. Ponieważ Django znany jest z asertywnych schematów nazewnictwa, ta metodologia jest zwykle bardzo przydatna.
Lorenz Lo Sauer
7
Zauważ, że wyjście querynie jest poprawnym SQL, ponieważ „Django nigdy nie interpoluje parametrów: wysyła zapytanie i parametry osobno do adaptera bazy danych, który wykonuje odpowiednie operacje”. Źródło: code.djangoproject.com/ticket/17741
gregoltsov
3
@AndreMiller należy używać stable, a nie dev, żeby dodać odnośnik do bieżącej wersji Django tak: docs.djangoproject.com/en/stable/faq/models/...
Flimm
3
django.db.connection.queries zwraca pustą listę
fantastory
61

Rozszerzenia Django mają polecenie shell_plus z parametremprint-sql

./manage.py shell_plus --print-sql

W powłoce django zostaną wydrukowane wszystkie wykonane zapytania

Dawny.:

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
źródło
1
Używam go z --print-sql lub z SHELL_PLUS_PRINT_SQL = True i to nie pomaga - nadal nie widzę zapytań. jakiś pomysł dlaczego? django 1.8
Dejell
1
Musisz ustawić DEBUG = True w ustawieniach.py, aby zobaczyć zapytania
Konstantin Voschanov,
50

Spójrz na debug_toolbar , jest to bardzo przydatne do debugowania.

Dokumentacja i źródło są dostępne na stronie http://django-debug-toolbar.readthedocs.io/ .

Zrzut ekranu paska narzędzi debugowania

Glader
źródło
1
debug_toolbar jest szczególnie użyteczny, gdy masz zapytanie, które kończy się błędem składni SQL; wyświetli ostatnie zapytanie, które próbowało się uruchomić (i nie powiodło się), co ułatwia debugowanie.
scoopseven
Jedyne, co widać w zapytaniach SQL w przeglądarce. Jeśli uruchamiasz testy z terminala i chcesz je tam zobaczyć, nie jest to realne rozwiązanie. Wciąż świetnie, używam go do dziś.
Erdin Eray,
24
q = Query.objects.values('val1','val2','val_etc')

print q.query
jgabrielsk8
źródło
bardzo prosta odpowiedź! Nicea
Espoir Murhabazi
Czy ta funkcjonalność została usunięta? To nie działa, kiedy to m = MyModel.objects.get(...)następujem.query
sg
To dlatego, że mnie jest już zestawem zapytań. Zastosowanie q = MyModel.objects.filter(...), a następnie q.query, po czym m = q.get().
Brouwer
24

Żadna inna odpowiedź nie obejmuje tej metody, więc:

Uważam, że najbardziej użyteczną, prostą i niezawodną metodą jest zapytanie bazy danych. Na przykład w systemie Linux dla Postgres możesz:

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

Każda baza danych będzie miała nieco inną procedurę. W dziennikach bazy danych zobaczysz nie tylko nieprzetworzony kod SQL, ale także wszelkie ustawienia połączenia lub transakcji narzucone przez django w systemie.

Bryce
źródło
8
nie zapomnij, aby ustawić log_statement='all'się postgresql.confna tej metodzie.
RickyA
2
Możesz go znaleźć postgresql.conf, biegającpsql -U postgres -c 'SHOW config_file'
kramer65
17

Chociaż możesz to zrobić za pomocą dostarczonego kodu, uważam, że korzystanie z aplikacji paska narzędzi debugowania jest doskonałym narzędziem do wyświetlania zapytań. Możesz pobrać go z github tutaj .

Daje to opcję pokazania wszystkich zapytań uruchomionych na danej stronie wraz z czasem potrzebnym na zapytanie. Podsumowuje także liczbę zapytań na stronie oraz całkowity czas na szybką recenzję. To świetne narzędzie, gdy chcesz zobaczyć, co robi Django ORM za kulisami. Ma również wiele innych fajnych funkcji, z których możesz skorzystać, jeśli chcesz.

googletorp
źródło
2
Wydaje
philfreo
15

Inna opcja, patrz opcje rejestrowania w pliku settings.py opisane w tym poście

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

debug_toolbar spowalnia każde ładowanie strony na serwerze deweloperskim, logowanie nie jest tak szybsze. Dane wyjściowe można zrzucić do konsoli lub pliku, więc interfejs użytkownika nie jest tak przyjemny. Ale w przypadku widoków z dużą ilością SQL-ów debugowanie i optymalizacja SQLów przez debug_toolbar może zająć dużo czasu, ponieważ ładowanie każdej strony jest tak wolne.

Przetaktowany
źródło
Świetny! Chociaż pasek narzędzi wygląda świetnie, myślę, że ta odpowiedź powinna być zaakceptowana. Jest to rozwiązanie, które chciałem, ponieważ pozwala „manage.py runserver” zalogować SQL do konsoli i współpracuje z „manage.py migrować”. To ostatnie pozwoliło mi zobaczyć, że „przy kasowaniu kasowania” zdecydowanie nie było ustawione, kiedy tworzone są moje tabele. Warto zauważyć, że ta odpowiedź jest oparta na docs.djangoproject.com/en/1.9/topics/logging/…
LS
10

Jeśli upewnisz się, że plik settings.py ma:

  1. django.core.context_processors.debug wymienione w CONTEXT_PROCESSORS
  2. DEBUG=True
  3. jesteś IPw INTERNAL_IPSkrotce

Powinieneś mieć dostęp do sql_querieszmiennej. Do każdej strony, która wygląda tak, dołączam stopkę:

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

Mam zmienną sql_time_sum, dodając linię

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

do funkcji debugowania w django_src / django / core / context_processors.py.

Mike Howsden
źródło
1
Właśnie próbowałem tego i (po usunięciu części sql_time_sum) otrzymałem: Brak nazwanych cykli w szablonie. „nieparzyste, parzyste” nie jest zdefiniowane - czego mi brakuje?
rozbitek
8

W tym celu opracowałem rozszerzenie, dzięki czemu można łatwo umieścić dekorator w funkcji widoku i zobaczyć, ile zapytań jest wykonywanych.

Żeby zainstalować:

$ pip install django-print-sql

Aby użyć jako menedżera kontekstu:

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

Aby użyć jako dekoratora:

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

królik. aaron
źródło
3

Uważam, że powinno to działać, jeśli używasz 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())}')
cedzak
źródło
Działa to nawet w Pythonie 2. Wystarczy tylko refaktor, taki jak print (kursor.mogrify (* qs.query.sql_with_params ())).
iChux,
IIRC Cursor.mogrify zwraca ciąg znaków, więc przypuszczam, że użycie łańcucha f do formatowania jest zbędne.
chander
2

Poniższe zwraca zapytanie jako poprawny SQL na podstawie 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 '):]
Lampa błyskowa
źródło
2

Zrobiłem mały fragment, którego możesz użyć:

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)

Jako parametr przyjmuje (funkcja kwerend SQL) kontrolę i argumenty, kwargs potrzebne do wywołania tej funkcji. W rezultacie zwraca, która funkcja zwraca, i drukuje zapytania SQL w konsoli.

turkus
źródło
1

Umieszczam tę funkcję w pliku util w jednej z aplikacji w moim projekcie:

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

Następnie, w razie potrzeby, po prostu importuję go i wywołuję z dowolnego kontekstu (zwykle widok), na przykład:

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

Fajnie jest robić to poza szablonem, ponieważ wtedy, jeśli masz widoki API (zwykle Django Rest Framework), ma to również zastosowanie.

getup8
źródło
1

W przypadku Django 2.2:

Ponieważ większość odpowiedzi niewiele mi pomogła podczas korzystania ./manage.py shell. W końcu znalazłem odpowiedź. Mam nadzieję, że to komuś pomoże.

Aby wyświetlić wszystkie zapytania:

from django.db import connection
connection.queries

Aby wyświetlić zapytanie dla pojedynczego zapytania:

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

q.querypo prostu wyświetlam obiekt dla mnie. Za pomocą __str__()(reprezentacja ciągu) wyświetlono pełne zapytanie.

goutham_mi3
źródło
0

Przeglądaj zapytania za pomocą django.db.connection.queries

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

Uzyskaj dostęp do surowego zapytania SQL na obiekcie QuerySet

 qs = MyModel.objects.all()
 print(qs.query)
Muhammad Parwej
źródło
0

Wystarczy dodać w django, jeśli masz zapytanie takie jak:

MyModel.objects.all()

zrobić:

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

aby uzyskać ciąg SQL

Robert Wallace
źródło