django как увидеть sql-запрос при запуске тестов?

Один из моих модульных тестов приложения django не работает с

DatabaseError: ORA-00942: table or view does not exist

Я хотел бы увидеть реальный SQL-запрос, вызвавший эту ошибку. Вы знаете, как этого добиться?


person mnowotka    schedule 31.10.2012    source источник
comment
Не совсем. Я не хочу включать «print connection.queries» в тестовый пример, потому что для выполнения этой строки мне нужно сначала перехватить исключение. Если я поймаю это исключение, тест пройдет, что нехорошо. Повторное повышение этого исключения не очень элегантно, я ищу лучшее решение.   -  person mnowotka    schedule 31.10.2012
comment
Другое дело, что печать с тестами не работает - по крайней мере, у меня ...   -  person mnowotka    schedule 31.10.2012
comment
Так или иначе, вам придется перехватить исключение, чтобы отобразить любую информацию в момент ошибки. Я не вижу ничего неэлегантного в повторном вызове исключения - просто используйте ключевое слово raise само по себе, и оно пройдет с неповрежденной трассировкой стека.   -  person Andrew Gorcester    schedule 31.10.2012


Ответы (9)


Если вы хотите распечатать / записать все запросы SQL из тестов, попробуйте создать подкласс TestCase следующим образом:

from django.conf import settings
from django.template import Template, Context
import sys
from django.db import connection
from django.test import TestCase

class LoggingTestCase(TestCase):

  @staticmethod
  def setUpClass():
    # The test runner sets DEBUG to False. Set to True to enable SQL logging.
    settings.DEBUG = True
    super(LoggingTestCase, LoggingTestCase).setUpClass()

  @staticmethod
  def tearDownClass():
    super(LoggingTestCase, LoggingTestCase).tearDownClass()

    time = sum([float(q['time']) for q in connection.queries])
    t = Template("{{count}} quer{{count|pluralize:\"y,ies\"}} in {{time}} seconds:\n\n{% for sql in sqllog %}[{{forloop.counter}}] {{sql.time}}s: {{sql.sql|safe}}{% if not forloop.last %}\n\n{% endif %}{% endfor %}")
    print >> sys.stderr, t.render(Context({'sqllog': connection.queries, 'count': len(connection.queries), 'time': time}))

    # Empty the query list between TestCases.    
    connection.queries = []

Затем используйте LoggingTestCase вместо TestCase в качестве базового класса в ваших тестах. Просто не забудьте называть это tearDownClass, если вы его переопределите.

person tuomassalo    schedule 28.08.2013
comment
Вы также должны вызвать super setUpClass. Когда вы этого не сделаете, чего-то не хватает, например, загрузки приспособлений. - person arsenbonbon; 06.10.2015
comment
@arsenbonbon хороший момент, теперь исправлено. Если вы проголосовали против, рассмотрите возможность отмены голосования, см. stackoverflow.com/help/privileges/vote-down - person tuomassalo; 06.10.2015
comment
Django действительно не позволяет вам установить переменную окружения для вывода всех запросов? - person Andy; 03.11.2016
comment

Вы также можете сделать следующее, чтобы получить запросы (а затем, например, распечатать его или оценить в своем тесте).

На самом деле вы в настоящее время не должны изменять django.conf.settings, поэтому Я использую override_settings.

from django.db import connection, reset_queries
from django.test import override_settings, TransactionTestCase

class TransactionTests(TransactionTestCase):

    @override_settings(DEBUG=True)
    def test_sql(self):
        reset_queries()
        try:
            # Code that uses the ORM goes here
        except Exception as e:
            pass
        self.assertEqual(connection.queries, [])

TestCase также может подойти, см. Различия в этом ответе.

См. документация Django для получения подробной информации о выводе SQL.

person yofee    schedule 19.04.2018


Другой вариант - использовать connection.execute_wrapper() в вашем проверить следующим образом:

from django.db import connection

def logger(execute, sql, params, many, context):
    print(sql, params)
    return execute(sql, params, many, context)

class GizmoTest(TestCase):

    def test_with_sql_logging(self):
        with connection.execute_wrapper(logger):
            code_that_uses_database()

Протестировано с Django 2.2.

person Eerik Sven Puudist    schedule 13.07.2019

Лучшее решение, которое я нашел до сих пор, - это настраиваемая команда управления django debugsqlshell, предоставляемая django-debugtoolbar.

person mnowotka    schedule 26.11.2012
comment
Не могли бы вы подробнее рассказать, как использовать команду debugsqlshell для запуска теста. Это не объясняется в документации django-debugtoolbar. - person gogognome; 18.07.2019
comment
@gogognome, это не так, я считаю, что mnowotka неправильно поняла вопрос. Ну, на самом деле, только что выяснил, что он TS, поэтому здесь -1 за неверно принятый ответ - person Eugene K; 25.08.2020

Это не самое чистое решение, но если вы просто хотите быстро отладить без установки дополнительных пакетов, вы можете поискать метод execute () в django / db.

Для Oracle, я думаю, он находится в:

django / db / backends / oracle / base.py и найдите:

def execute

Для PostgreSQL он находится в:

django / db / backends / postgresql_psycopg2 / base.py

В CursorWrapper есть метод execute ().

Оба отлавливают IntegrityError и DatabaseError, вы можете добавить туда оператор печати.

Для пользователей, которые хотят видеть все запросы sql, поместите оператор печати сразу после вызова функции.

person kev    schedule 30.08.2013

Вы можете изменить уровень консоли на DEBUG в настройках. Он работал на Django 1.9.

LOGGING = {
...
'handlers': {
    'console': {
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
        },
    }
...
}
person tmin    schedule 22.08.2017
comment
Не работает По соображениям производительности ведение журнала SQL включается только тогда, когда для параметра settings.DEBUG установлено значение True, независимо от уровня ведения журнала или установленных обработчиков. docs.djangoproject.com/en/1.11/topics/logging / - person pymen; 16.12.2019

В случае pytest и pytest-django просто создайте для него приспособление.

@pytest.fixture
def debug_queries(db):
    """ Because pytest run tests with DEBUG=False
        the regular query logging will not work, use this fixture instead
    """
    from django.db import connection
    from django.test.utils import CaptureQueriesContext
    with CaptureQueriesContext(connection):
        yield connection

тогда в ваших тестах

@pytest.mark.django_db
def test__queries(debug_queries):
    # run your queries here

Конечно, ваша конфигурация ведения журнала должна разрешать регистрацию запросов, примерно так:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '%(asctime)s - %(levelname)s - %(name)s - %(message)s',
        },
    },
    'handlers': {
        'default': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'standard',
            'stream': 'ext://sys.stdout',
        },
    },
    'loggers': {
        'django.db.backends': {
            'level': 'DEBUG',
            'handlers': ['default'],
            'propagate': False,
        },
    }
}
person Eugene K    schedule 25.08.2020
comment
SQL-запросы не распечатываются. - person Suor; 27.08.2020

Это было решение, которое сработало для меня (Django 3.1):

from django.test import TestCase


class TestSomething(TestCase):
    @override_settings(DEBUG=True)
    def test_something(self):
        pass
    
    def tearDown(self):
        from django.db import connection
        for query in connection.queries:
            print(f"✅ {query['sql']}\n")

источник

person Matthew Hegarty    schedule 22.09.2020