django 在运行测试时如何查看 sql 查询?

Posted

技术标签:

【中文标题】django 在运行测试时如何查看 sql 查询?【英文标题】:django how to see sql query when running tests? 【发布时间】:2012-10-21 04:42:20 【问题描述】:

我的一个 django 应用程序单元测试失败了

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

我想查看导致此错误的实际 SQL 查询。你知道如何实现吗?

【问题讨论】:

这有用吗? ***.com/questions/1074212/… 不完全是。我不想在测试用例中包含“print connection.queries”,因为为了执行该行,我需要首先捕获异常。如果我发现该异常,则测试将通过,这是不好的。重新提出这个异常不是很优雅,我正在寻找更好的解决方案。 另一件事是“打印”不适用于测试 - 至少对我来说...... 您必须以一种或另一种方式捕获异常,以便在错误发生时显示任何信息。我没有看到重新引发异常有任何不雅之处——只需单独使用 raise 关键字,它就会在堆栈跟踪完好无损的情况下通过。 哦,实际上,我想还有另一种解决方案——您可以在 DEBUG 级别记录并配置您的记录器,以便在发生时将所有 SQL 查询写入日志。见docs.djangoproject.com/en/dev/topics/logging 【参考方案1】:

目前我找到的最好的解决方案是django-debugtoolbar提供的debugsqlshell自定义django管理命令。

【讨论】:

能否详细说明如何使用debugsqlshell 命令运行测试。这在 django-debugtoolbar 的文档中没有解释。 @gogognome 我相信 mnowotka 没有误解这个问题。好吧,实际上,刚刚发现他是 TS,所以这里是 -1 表示错误接受的答案【参考方案2】:

如果您想打印/记录测试中的所有 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 quercount|pluralize:\"y,ies\" in time seconds:\n\n% for sql in sqllog %[forloop.counter] sql.times: 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

【讨论】:

您也应该调用超级 setUpClass。如果不这样做,就会丢失一些东西,例如夹具加载。 @arsenbonbon 好点,现在已修复。如果您投了反对票,请考虑取消投票,请参阅:***.com/help/privileges/vote-down Django 真的不允许你设置一些环境变量来打印所有的查询吗? How to override settings for tests.【参考方案3】:

它不是最干净的解决方案,但如果你只是想快速调试而不安装额外的包,你可以在 django/db 中寻找 execute() 方法。

对于 Oracle 我猜它在:

django/db/backends/oracle/base.py 并寻找:

def execute

对于 PostgreSQL,它位于:

django/db/backends/postgresql_psycopg2/base.py

在 CursorWrapper 中有一个 execute() 方法。

两者都在捕获 IntegrityError 和 DatabaseError,您可以在此处添加打印语句。

对于想查看所有sql查询的人,将打印语句放在函数调用之后。

【讨论】:

【参考方案4】:

您可以在设置中将控制台级别更改为调试。它适用于 Django 1.9。

LOGGING = 
...
'handlers': 
    'console': 
        'level': 'DEBUG',
        'class': 'logging.StreamHandler',
        'formatter': 'simple'
        ,
    
...

【讨论】:

不工作“出于性能原因,SQL 日志记录仅在 settings.DEBUG 设置为 True 时启用,无论日志记录级别或已安装的处理程序如何。” docs.djangoproject.com/en/1.11/topics/logging/…【参考方案5】:

您还可以执行以下操作来获取查询(然后例如打印它或在您的测试中评估它)。

现在其实你shouldn't alter 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 也可能适用,请参阅此answer 中的差异。

有关 SQL 输出的详细信息,请参阅Django documentation。

【讨论】:

【参考方案6】:

另一种选择是在您的测试中使用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 测试。

【讨论】:

【参考方案7】:

另一种选择是使用CaptureQueriesContext(使用pytest 测试)。

from django.db import connection
from django.test.utils import CaptureQueriesContext


def test_foo():
    with CaptureQueriesContext(connection) as ctx:
        # code that runs SQL queries
        print(ctx.captured_queries)

来源:

https://blog.ploetzli.ch/2019/showing-sql-queries-with-pytest-and-django/ How to force django to print each executed sql query

【讨论】:

这是最简单、最干净的答案,因为它可以让您捕获任意小段代码的查询,如果您想解决问题或优化查询,这就是您想要的。【参考方案8】:

pytestpytest-django 的情况下,只需为它创建一个fixture

@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,
        ,
    

【讨论】:

SQL 查询没有打印出来。 这对我有用!你知道是否可以使用pytest-django 中的settings 夹具来设置测试记录器?我尝试使用它,但在这种情况下它不适用于登录。 @oglop 不确定您所说的using settings fixture 是什么意思。你到底想达到什么目的? @EugeneK pytest-django 具有可以覆盖设置pytest-django.readthedocs.io/en/latest/… 的设置固定装置我在想也许我可以将它用作debug_queries 的参数所以我不需要添加日志配置test.settings.py 并在需要时使用夹具。 @oglop 您始终可以在debug_queries 夹具内将记录器级别设置为调试,无需破解设置。灯具退出时别忘了恢复关卡【参考方案9】:

这是对我有用的解决方案(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")

source

【讨论】:

以上是关于django 在运行测试时如何查看 sql 查询?的主要内容,如果未能解决你的问题,请参考以下文章

运行 Django 测试时如何查看标准输出?

运行 Django 测试时如何查看标准输出?

查看 Django 查询集删除的 SQL 查询

Django模型层相关

[Django] 查看orm自己主动运行的原始查询sql

如何计算呈现 Django 管理页面所需的 SQL 查询数?