Skip to content

Commit fb6fa96

Browse files
committed
Add django_debug_sql ini option
TODO: - [ ] doc - [ ] use a command line option instead? I've thought about having a fixture also, but it would require to set "force_debug_cursor" on the connections then, and it probably not useful; typically you want to use this for a short time only - therefore a command line option might be better suited also (but you can use `-o django_debug_sql = 1`). And on the other hand, it will only show up on failures, and is therefore maybe good to set it in general.
1 parent ce5d5bc commit fb6fa96

File tree

3 files changed

+101
-3
lines changed

3 files changed

+101
-3
lines changed

Diff for: pytest_django/fixtures.py

+32-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import with_statement
44

5+
import logging
56
import os
67
import warnings
78
from contextlib import contextmanager
@@ -80,6 +81,27 @@ def django_db_createdb(request):
8081
return request.config.getvalue("create_db")
8182

8283

84+
def _setup_sql_debug_logging():
85+
# Simulate what Django's DebugSQLTextTestResult does.
86+
logger = logging.getLogger('django.db.backends')
87+
oldlevel = logger.level
88+
logger.setLevel(logging.DEBUG)
89+
return oldlevel
90+
91+
92+
def _restore_sql_debug_logging(request, oldlevel):
93+
logger = logging.getLogger('django.db.backends')
94+
if logger.level != logging.DEBUG:
95+
request.node.warn(pytest.PytestWarning(
96+
"Debug logging level of django.db.backends was changed (to {}). "
97+
"SQL queries might be missing. "
98+
"This might be caused by calling django.setup() too late/unnecessarily.".format(
99+
logger.level
100+
)
101+
))
102+
logger.setLevel(oldlevel)
103+
104+
83105
@pytest.fixture(scope="session")
84106
def django_db_setup(
85107
request,
@@ -95,6 +117,11 @@ def django_db_setup(
95117

96118
setup_databases_args = {}
97119

120+
debug_sql = request.config.getini("django_debug_sql")
121+
if debug_sql:
122+
setup_databases_args["debug_sql"] = True
123+
old_loglevel = _setup_sql_debug_logging()
124+
98125
if not django_db_use_migrations:
99126
_disable_native_migrations()
100127

@@ -108,7 +135,9 @@ def django_db_setup(
108135
**setup_databases_args
109136
)
110137

111-
def teardown_database():
138+
yield
139+
140+
if not django_db_keepdb:
112141
with django_db_blocker.unblock():
113142
try:
114143
teardown_databases(db_cfg, verbosity=request.config.option.verbose)
@@ -119,8 +148,8 @@ def teardown_database():
119148
)
120149
)
121150

122-
if not django_db_keepdb:
123-
request.addfinalizer(teardown_database)
151+
if debug_sql:
152+
_restore_sql_debug_logging(request, old_loglevel)
124153

125154

126155
def _django_db_fixture_helper(

Diff for: pytest_django/plugin.py

+6
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ def pytest_addoption(parser):
126126
type="bool",
127127
default=True,
128128
)
129+
parser.addini(
130+
"django_debug_sql",
131+
"Enable logging of SQL statements, displayed with failed tests.",
132+
type="bool",
133+
default=False,
134+
)
129135
group._addoption(
130136
"--fail-on-template-vars",
131137
action="store_true",

Diff for: tests/test_db_debug.py

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
def test_debug_sql_setting(django_testdir):
2+
django_testdir.create_test_module(
3+
"""
4+
import pytest
5+
6+
from .app.models import Item
7+
8+
@pytest.mark.django_db
9+
def test_fail_with_db_queries():
10+
assert Item.objects.count() == 0
11+
assert 0, "triggered failure"
12+
"""
13+
)
14+
django_testdir.makeini(
15+
"""
16+
[pytest]
17+
django_debug_sql = 1
18+
"""
19+
)
20+
21+
result = django_testdir.runpytest_subprocess()
22+
assert result.ret == 1
23+
result.stdout.fnmatch_lines([
24+
"*- Captured log setup -*",
25+
"*CREATE TABLE*",
26+
"*- Captured log call -*",
27+
"*SELECT COUNT*",
28+
])
29+
30+
31+
def test_debug_sql_with_django_setup(django_testdir):
32+
django_testdir.create_test_module(
33+
"""
34+
import pytest
35+
36+
from .app.models import Item
37+
38+
@pytest.mark.django_db
39+
def test_fail_with_db_queries():
40+
import django
41+
django.setup()
42+
43+
assert Item.objects.count() == 0
44+
assert 0, "triggered failure"
45+
"""
46+
)
47+
django_testdir.makeini(
48+
"""
49+
[pytest]
50+
django_debug_sql = 1
51+
"""
52+
)
53+
54+
result = django_testdir.runpytest_subprocess()
55+
assert result.ret == 1
56+
result.stdout.fnmatch_lines([
57+
# "*- Captured stdout setup -*",
58+
"*- Captured log setup -*",
59+
"*CREATE TABLE*",
60+
"*= warnings summary =*",
61+
"*Debug logging level of django.db.backends was changed (to 0).*",
62+
])
63+
assert "SELECT COUNT" not in result.stdout.str()

0 commit comments

Comments
 (0)