Як я можу побачити необроблені запити SQL, які працює Django?


307

Чи є спосіб показати SQL, що Django працює під час виконання запиту?

Відповіді:


372

Див. Поширені запитання щодо документів: " Як я можу побачити необроблені запити SQL, які працює Django? "

django.db.connection.queries містить список запитів SQL:

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

Набори запитів також мають queryатрибут, що містить запит, який потрібно виконати:

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

Зауважте, що вихід запиту не є дійсним SQL, оскільки:

"Django ніколи фактично не інтерполює параметри: він надсилає запит та параметри окремо на адаптер бази даних, який виконує відповідні операції."

З повідомлення про помилку Django # 17741 .

Через це не слід надсилати запити безпосередньо в базу даних.


13
Щоб підтвердити цю відповідь, слід скористатися поточною версією документації Django: docs.djangoproject.com/en/dev/faq/models/…
Андре Міллер

5
Чудова відповідь. Однак рекомендується використовувати задану, вбудовану в str()функцію пітона , яка викликає внутрішній __str__()метод. Наприклад, str(MyModel.objects.filter(name="my name").query) я б також рекомендував використовувати IPython та оболонку Django для вашого проекту. Завершення вкладки потім забезпечує самоаналіз об'єкта. Оскільки Джанго відомий своїми твердими схемами називання, ця методологія, як правило, дуже корисна.
Лоренц Ло Зауер

7
Зауважте, що висновок queryне є дійсним SQL, оскільки "Django ніколи фактично не інтерполює параметри: він надсилає запит та параметри окремо на адаптер бази даних, який виконує відповідні операції." Джерело: code.djangoproject.com/ticket/17741
gregoltsov

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 всі виконані запити будуть надруковані

Наприклад:

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, і це не допомагає - я все одно не бачу запитів. будь-яка ідея чому? django 1.8
Dejell

1
Потрібно встановити DEBUG = True у your settings.py, щоб побачити запити
Костянтин Вощанов

50

Погляньте на debug_toolbar , це дуже корисно для налагодження.

Документація та джерело доступні на веб- сайті http://django-debug-toolbar.readthedocs.io/ .

Знімок екрана панелі інструментів налагодження


1
debug_toolbar особливо корисний, коли у вас є запит, який не вдається з помилкою синтаксису SQL; він відобразить останній запит, який намагався запустити (і не вдався), полегшивши налагодження.
Scoopseven

Єдине, що ви бачите SQL-запити у браузері. Якщо ви запускаєте тести з терміналу і хочете побачити їх там, це не є прийнятним рішенням. Досі чудово, я використовую його донині.
Ердін

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

print q.query

дуже проста відповідь! Ніцца
Еспоар Мурбабазі

Видалено цю функціональність? Це не працює, коли я m = MyModel.objects.get(...)супроводжуюсьm.query
sg

Це тому, що 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 за лаштунками. Він також має багато інших приємних функцій, якими ви можете користуватися, якщо хочете.


2
Мені здається, це найкраща версія: github.com/django-debug-toolbar/django-debug-toolbar
philfreo

15

Інший варіант, див. Параметри журналу в налаштуваннях.py, описані цією публікацією

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

debug_toolbar уповільнює завантаження кожної сторінки на вашому сервері розробок, реєстрація не є швидшою. Виходи можна скинути на консоль чи файл, тому інтерфейс користувача не такий приємний. Але для переглядів з великою кількістю SQL може знадобитися тривалий час налагодження та оптимізації SQL через debug_toolbar, оскільки завантаження кожної сторінки відбувається настільки повільно.


Відмінно! Хоча панель інструментів виглядає чудово, я думаю, що ця відповідь повинна бути прийнятою. Це рішення, яке я хотів, тому що він дозволяє "управляти.py runserver" журналом SQL на консоль, і він працює з "Manag.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


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

Це працювало навіть у 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-запити в консолі.


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), це також застосовно.


1

Для Джанго 2.2:

Оскільки більшість відповідей мені не дуже допомогли при використанні ./manage.py shell. Нарешті я знайшов відповідь. Сподіваюсь, це комусь допомагає.

Щоб переглянути всі запити:

from django.db import connection
connection.queries

Щоб переглянути запит на один запит:

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

q.queryпросто показ об’єкта для мене. За допомогою __str__()(String уявлення) відображається повний запит.


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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.