Skip to content

Commit d6b9563

Browse files
committed
Arrange db fixtures in a more extensible way
The current scheme is not conducive to adding additional modifier fixtures similar to ``django_db_reset_sequence``, such as a fixture for ``serialized_rollback`` or for specifying databases. Instead, arrange it such that there is a base helper fixture `_django_db_helper` which does all the work, and the other fixtures merely exist to modify it.
1 parent eeeb163 commit d6b9563

File tree

3 files changed

+54
-84
lines changed

3 files changed

+54
-84
lines changed

pytest_django/fixtures.py

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -138,24 +138,30 @@ def teardown_database() -> None:
138138
request.addfinalizer(teardown_database)
139139

140140

141-
def _django_db_fixture_helper(
141+
@pytest.fixture()
142+
def _django_db_helper(
142143
request,
144+
django_db_setup: None,
143145
django_db_blocker,
144-
transactional: bool = False,
145-
reset_sequences: bool = False,
146146
) -> None:
147147
from django import VERSION
148148

149149
if is_django_unittest(request):
150150
return
151151

152-
if not transactional and "live_server" in request.fixturenames:
153-
# Do nothing, we get called with transactional=True, too.
154-
return
152+
marker = request.node.get_closest_marker("django_db")
153+
if marker:
154+
transactional, reset_sequences, _databases = validate_django_db(marker)
155+
else:
156+
transactional, reset_sequences, _databases = False, False, None
155157

156-
_databases = getattr(
157-
request.node, "_pytest_django_databases", None,
158-
) # type: Optional[_DjangoDbDatabases]
158+
transactional = transactional or (
159+
"transactional_db" in request.fixturenames
160+
or "live_server" in request.fixturenames
161+
)
162+
reset_sequences = reset_sequences or (
163+
"django_db_reset_sequences" in request.fixturenames
164+
)
159165

160166
django_db_blocker.unblock()
161167
request.addfinalizer(django_db_blocker.restore)
@@ -186,6 +192,26 @@ class PytestDjangoTestCase(test_case_class): # type: ignore[misc,valid-type]
186192
request.addfinalizer(test_case._post_teardown)
187193

188194

195+
def validate_django_db(marker) -> "_DjangoDb":
196+
"""Validate the django_db marker.
197+
198+
It checks the signature and creates the ``transaction``,
199+
``reset_sequences`` and ``databases`` attributes on the marker
200+
which will have the correct values.
201+
202+
A sequence reset is only allowed when combined with a transaction.
203+
"""
204+
205+
def apifun(
206+
transaction: bool = False,
207+
reset_sequences: bool = False,
208+
databases: "_DjangoDbDatabases" = None,
209+
) -> "_DjangoDb":
210+
return transaction, reset_sequences, databases
211+
212+
return apifun(*marker.args, **marker.kwargs)
213+
214+
189215
def _disable_migrations() -> None:
190216
from django.conf import settings
191217
from django.core.management.commands import migrate
@@ -229,41 +255,24 @@ def _set_suffix_to_test_databases(suffix: str) -> None:
229255

230256

231257
@pytest.fixture(scope="function")
232-
def db(
233-
request,
234-
django_db_setup: None,
235-
django_db_blocker,
236-
) -> None:
258+
def db(_django_db_helper: None) -> None:
237259
"""Require a django test database.
238260
239261
This database will be setup with the default fixtures and will have
240262
the transaction management disabled. At the end of the test the outer
241263
transaction that wraps the test itself will be rolled back to undo any
242264
changes to the database (in case the backend supports transactions).
243-
This is more limited than the ``transactional_db`` resource but
265+
This is more limited than the ``transactional_db`` fixture but
244266
faster.
245267
246-
If multiple database fixtures are requested, they take precedence
247-
over each other in the following order (the last one wins): ``db``,
248-
``transactional_db``, ``django_db_reset_sequences``.
268+
If both ``db`` and ``transactional_db`` are requested,
269+
``transactional_db`` takes precedence.
249270
"""
250-
if "django_db_reset_sequences" in request.fixturenames:
251-
request.getfixturevalue("django_db_reset_sequences")
252-
if (
253-
"transactional_db" in request.fixturenames
254-
or "live_server" in request.fixturenames
255-
):
256-
request.getfixturevalue("transactional_db")
257-
else:
258-
_django_db_fixture_helper(request, django_db_blocker, transactional=False)
271+
# The `_django_db_helper` fixture checks if `db` is requested.
259272

260273

261274
@pytest.fixture(scope="function")
262-
def transactional_db(
263-
request,
264-
django_db_setup: None,
265-
django_db_blocker,
266-
) -> None:
275+
def transactional_db(_django_db_helper: None) -> None:
267276
"""Require a django test database with transaction support.
268277
269278
This will re-initialise the django database for each test and is
@@ -272,35 +281,26 @@ def transactional_db(
272281
If you want to use the database with transactions you must request
273282
this resource.
274283
275-
If multiple database fixtures are requested, they take precedence
276-
over each other in the following order (the last one wins): ``db``,
277-
``transactional_db``, ``django_db_reset_sequences``.
284+
If both ``db`` and ``transactional_db`` are requested,
285+
``transactional_db`` takes precedence.
278286
"""
279-
if "django_db_reset_sequences" in request.fixturenames:
280-
request.getfixturevalue("django_db_reset_sequences")
281-
_django_db_fixture_helper(request, django_db_blocker, transactional=True)
287+
# The `_django_db_helper` fixture checks if `transactional_db` is requested.
282288

283289

284290
@pytest.fixture(scope="function")
285291
def django_db_reset_sequences(
286-
request,
287-
django_db_setup: None,
288-
django_db_blocker,
292+
_django_db_helper: None,
293+
transactional_db: None,
289294
) -> None:
290295
"""Require a transactional test database with sequence reset support.
291296
292-
This behaves like the ``transactional_db`` fixture, with the addition
293-
of enforcing a reset of all auto increment sequences. If the enquiring
297+
This requests the ``transactional_db`` fixture, and additionally
298+
enforces a reset of all auto increment sequences. If the enquiring
294299
test relies on such values (e.g. ids as primary keys), you should
295300
request this resource to ensure they are consistent across tests.
296-
297-
If multiple database fixtures are requested, they take precedence
298-
over each other in the following order (the last one wins): ``db``,
299-
``transactional_db``, ``django_db_reset_sequences``.
300301
"""
301-
_django_db_fixture_helper(
302-
request, django_db_blocker, transactional=True, reset_sequences=True
303-
)
302+
# The `_django_db_helper` fixture checks if `django_db_reset_sequences`
303+
# is requested.
304304

305305

306306
@pytest.fixture()

pytest_django/plugin.py

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import pytest
1616

1717
from .django_compat import is_django_unittest # noqa
18+
from .fixtures import _django_db_helper # noqa
1819
from .fixtures import _live_server_helper # noqa
1920
from .fixtures import admin_client # noqa
2021
from .fixtures import admin_user # noqa
@@ -40,6 +41,7 @@
4041
from .fixtures import rf # noqa
4142
from .fixtures import settings # noqa
4243
from .fixtures import transactional_db # noqa
44+
from .fixtures import validate_django_db
4345
from .lazy_django import django_settings_is_configured, skip_if_no_django
4446

4547

@@ -49,8 +51,6 @@
4951

5052
import django
5153

52-
from .fixtures import _DjangoDb, _DjangoDbDatabases
53-
5454

5555
SETTINGS_MODULE_ENV = "DJANGO_SETTINGS_MODULE"
5656
CONFIGURATION_ENV = "DJANGO_CONFIGURATION"
@@ -464,17 +464,7 @@ def _django_db_marker(request) -> None:
464464
"""
465465
marker = request.node.get_closest_marker("django_db")
466466
if marker:
467-
transaction, reset_sequences, databases = validate_django_db(marker)
468-
469-
# TODO: Use pytest Stash (item.stash) once that's stable.
470-
request.node._pytest_django_databases = databases
471-
472-
if reset_sequences:
473-
request.getfixturevalue("django_db_reset_sequences")
474-
elif transaction:
475-
request.getfixturevalue("transactional_db")
476-
else:
477-
request.getfixturevalue("db")
467+
request.getfixturevalue("_django_db_helper")
478468

479469

480470
@pytest.fixture(autouse=True, scope="class")
@@ -743,26 +733,6 @@ def restore(self) -> None:
743733
_blocking_manager = _DatabaseBlocker()
744734

745735

746-
def validate_django_db(marker) -> "_DjangoDb":
747-
"""Validate the django_db marker.
748-
749-
It checks the signature and creates the ``transaction``,
750-
``reset_sequences`` and ``databases`` attributes on the marker
751-
which will have the correct values.
752-
753-
A sequence reset is only allowed when combined with a transaction.
754-
"""
755-
756-
def apifun(
757-
transaction: bool = False,
758-
reset_sequences: bool = False,
759-
databases: "_DjangoDbDatabases" = None,
760-
) -> "_DjangoDb":
761-
return transaction, reset_sequences, databases
762-
763-
return apifun(*marker.args, **marker.kwargs)
764-
765-
766736
def validate_urls(marker) -> List[str]:
767737
"""Validate the urls marker.
768738

tests/test_db_setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ def test_run_second_transaction_test_case(self):
8080
"*test_run_first_django_test_case*",
8181
"*test_run_second_decorator*",
8282
"*test_run_second_fixture*",
83+
"*test_run_second_reset_sequences_fixture*",
8384
"*test_run_second_reset_sequences_decorator*",
8485
"*test_run_second_transaction_test_case*",
85-
"*test_run_second_reset_sequences_fixture*",
8686
"*test_run_last_test_case*",
8787
"*test_run_last_simple_test_case*",
8888
])

0 commit comments

Comments
 (0)