Как я могу увидеть необработанные запросы SQL, которые выполняет Django?

308

Есть ли способ показать SQL, который работает Django при выполнении запроса?

spence91
источник

Ответы:

373

См. Часто задаваемые вопросы по документам: « Как я могу увидеть необработанные SQL-запросы, выполняемые Django? »

django.db.connection.queries содержит список запросов SQL:

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

Querysets также имеют queryатрибут, содержащий запрос, который будет выполнен:

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

Обратите внимание, что вывод запроса не является допустимым SQL, потому что:

«На самом деле Django никогда не интерполирует параметры: он отправляет запрос и параметры отдельно адаптеру базы данных, который выполняет соответствующие операции».

Из сообщения об ошибке Django № 17741 .

Из-за этого вам не следует отправлять вывод запроса непосредственно в базу данных.

geowa4
источник
13
Чтобы подтвердить этот ответ на будущее, вам лучше связать текущую версию документации Django: docs.djangoproject.com/en/dev/faq/models/…
Андре Миллер
5
Отличный ответ. Тем не менее, рекомендуется использовать указанную встроенную str()функцию Pythonian , которая вызывает внутренний __str__()метод. Например, str(MyModel.objects.filter(name="my name").query) я бы также рекомендовал использовать IPython и оболочку Django вашего проекта. Завершение вкладки затем обеспечивает самоанализ объекта. Так как Django известен своими утвержденными схемами именования, эта методология имеет тенденцию быть очень полезной.
Лоренц Ло Зауэр
7
Обратите внимание, что вывод queryне является допустимым SQL, потому что «Django никогда не интерполирует параметры: он отправляет запрос и параметры отдельно адаптеру базы данных, который выполняет соответствующие операции». Источник: code.djangoproject.com/ticket/17741
Грегольцов
3
@AndreMiller Вы должны использовать stable, а не devссылку на текущую версию Django, например: docs.djangoproject.com/en/stable/faq/models/…
Flimm
3
django.db.connection.queries возвращает пустой список
фантастика
61

У Django-расширений есть команда shell_plus с параметромprint-sql

./manage.py shell_plus --print-sql

В django-shell все выполненные запросы будут напечатаны

Напр .:

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
Я использую его с --print-sql или с SHELL_PLUS_PRINT_SQL = True, и это не помогает - я все еще не вижу запросы. есть идеи почему?
Джанго
1
Вам нужно установить DEBUG = True в вашем settings.py, чтобы увидеть запросы
Константин
50

Посмотрите на debug_toolbar , он очень полезен для отладки.

Документация и источник доступны по адресу http://django-debug-toolbar.readthedocs.io/ .

Снимок экрана панели инструментов отладки

Glader
источник
1
debug_toolbar особенно полезен, когда у вас есть запрос, который терпит неудачу с ошибкой синтаксиса SQL; он будет отображать последний запрос, который был выполнен (и не выполнен), что облегчает его отладку.
scoopseven
Единственное, вы видите запросы SQL в браузере. Если вы запускаете тесты из терминала и хотите увидеть его там, это не является жизнеспособным решением. Все еще отлично, я использую это по сей день.
Erdin Эрай
24
q = Query.objects.values('val1','val2','val_etc')

print q.query
jgabrielsk8
источник
очень простой ответ! Ницца
Эспуар Мурхабази
Эта функциональность была удалена? Она не работает , когда я m = MyModel.objects.get(...)затемm.query
к.с.
Это потому, что mбольше не набор запросов. Используйте q = MyModel.objects.filter(...), тогда q.query, тогда m = q.get().
Брауэр
24

Ни один другой ответ не охватывает этот метод, поэтому:

Я считаю, что самый полезный, простой и надежный способ - спросить вашу базу данных. Например, в Linux для Postgres вы можете сделать:

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

У каждой базы данных будет немного другая процедура. В журналах базы данных вы увидите не только необработанный SQL, но и любые настройки соединения или накладные расходы на транзакции, которые django размещает в системе.

Брайс
источник
8
не забудьте установить log_statement='all'в postgresql.confтечение этого метода.
RickyA
2
Вы можете найти postgresql.confего, запустивpsql -U postgres -c 'SHOW config_file'
kramer65
17

Хотя вы можете сделать это с помощью поставляемого кода, я считаю, что использование приложения панели инструментов отладки - отличный инструмент для отображения запросов. Вы можете скачать его с github здесь .

Это дает вам возможность показать все запросы, выполненные на данной странице, а также время, затраченное на запрос. Он также суммирует количество запросов на странице вместе с общим временем для быстрого просмотра. Это отличный инструмент, когда вы хотите посмотреть, что делает Django ORM за кулисами. Он также имеет много других приятных функций, которые вы можете использовать, если хотите.

googletorp
источник
2
Похоже, это лучшая версия: github.com/django-debug-toolbar/django-debug-toolbar
philfreo
15

Другой вариант, см. Параметры ведения журнала в settings.py, описанном в этом посте.

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

debug_toolbar замедляет загрузку каждой страницы на вашем dev-сервере, ведение журнала - не так, это быстрее. Выходы могут быть выгружены в консоль или файл, поэтому пользовательский интерфейс не так хорош. Но для представлений с большим количеством SQL может потребоваться много времени для отладки и оптимизации SQL через debug_toolbar, так как загрузка каждой страницы очень медленная.

Разгон
источник
Превосходно! Хотя панель инструментов выглядит великолепно, я думаю, что этот ответ должен быть принятым. Это решение, которое я хотел, потому что он позволяет «manage.py runserver» регистрировать SQL на консоли и работает с «manage.py migrate». Последнее позволило мне увидеть, что «при удалении каскад» определенно не был установлен при создании моих таблиц. Стоит отметить, что этот ответ основан на docs.djangoproject.com/en/1.9/topics/logging/…
LS
10

Если вы убедитесь, что ваш файл settings.py имеет:

  1. django.core.context_processors.debug перечислены в CONTEXT_PROCESSORS
  2. DEBUG=True
  3. ваш IP в INTERNAL_IPSкортеже

Тогда вы должны иметь доступ к sql_queries переменной. Я добавляю нижний колонтитул к каждой странице, которая выглядит следующим образом:

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

Я получил переменную sql_time_sum , добавив строку

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

к функции отладки в django_src / django / core / context_processors.py.

Майк Хаусден
источник
1
Я только что попробовал это, и (удалив часть sql_time_sum), получил: Нет именованных циклов в шаблоне. «нечетное, четное» не определено - что мне не хватает?
отверженный
8

Я разработал расширение для этой цели, чтобы вы могли легко добавить декоратор в вашу функцию просмотра и посмотреть, сколько запросов выполнено.

Установить:

$ pip install django-print-sql

Для использования в качестве диспетчера контекста:

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

Для использования в качестве декоратора:

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
источник
3

Я считаю, что это должно работать, если вы используете 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())}')
Chander
источник
Это работало даже в Python 2. Только рефакторинг, такой как print (cursor.mogrify (* qs.query.sql_with_params ())) - все, что ему нужно.
iChux
IIRC Cursor.mogrify возвращает строку, поэтому я полагаю, что использование строки f для форматирования является излишним ..
chander
2

Следующее возвращает запрос как действительный SQL, основанный на 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

Я сделал небольшой фрагмент, который вы можете использовать:

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)

Он принимает в качестве параметров функцию (содержит SQL-запросы) для проверки и аргументов, kwargs, необходимых для вызова этой функции. В результате он возвращает то, что возвращает функция, и печатает SQL-запросы в консоли.

Turkus
источник
1

Я поместил эту функцию в файл утилит в одном из приложений в моем проекте:

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

Затем, когда это необходимо, я просто импортирую его и вызываю из любого необходимого контекста (обычно представления), например:

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

Это хорошо делать за пределами шаблона, потому что тогда, если у вас есть представления API (обычно Django Rest Framework), это применимо и там.

getup8
источник
1

Для Джанго 2.2:

Поскольку большинство ответов не очень помогли мне при использовании ./manage.py shell . Наконец я нашел ответ. Надеюсь, это кому-нибудь поможет.

Для просмотра всех запросов:

from django.db import connection
connection.queries

Чтобы просмотреть запрос для одного запроса:

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

q.queryпросто отображение объекта для меня. С помощью __str__()(String представление) отображается полный запрос.

goutham_mi3
источник
0

Просмотр запросов с использованием django.db.connection.queries

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

Доступ к необработанному SQL-запросу к объекту QuerySet

 qs = MyModel.objects.all()
 print(qs.query)
Мухаммед Парвэй
источник
0

Просто добавьте, в django, если у вас есть запрос вроде:

MyModel.objects.all()

делать:

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

получить строку sql

Роберт Уоллес
источник