diff --git a/docs/changelog.rst b/docs/changelog.rst index e1c8a982..baa550a2 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,13 @@ Compatibility * Official Django 6.1 support. +Bugfixes +^^^^^^^^ + +* Honor the ``django_db_serialized_rollback`` fixture when a test also has a + ``django_db`` marker, so data from migrations is restored between transactional + tests even when the marker does not enable ``serialized_rollback``. + v4.14.0 (2026-08-10) -------------------- diff --git a/pytest_django/fixtures.py b/pytest_django/fixtures.py index 2acf0e2f..1ea8cf23 100644 --- a/pytest_django/fixtures.py +++ b/pytest_django/fixtures.py @@ -134,10 +134,11 @@ def _get_databases_for_test(test: pytest.Item) -> tuple[Iterable[str], bool]: _available_apps, ) = validate_django_db(marker_db) elif "db" in fixtures or "transactional_db" in fixtures or "live_server" in fixtures: - serialized_rollback = "django_db_serialized_rollback" in fixtures + serialized_rollback = False databases = None else: return (), False + serialized_rollback = serialized_rollback or ("django_db_serialized_rollback" in fixtures) if databases is None: return (DEFAULT_DB_ALIAS,), serialized_rollback elif databases == "__all__": diff --git a/tests/test_database.py b/tests/test_database.py index aaff070e..c6f3b83c 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -179,6 +179,62 @@ def test_serialized_rollback_3(): result = django_pytester.runpytest_subprocess("-v") assert result.ret == 0 + @pytest.mark.parametrize( + "marker", + [ + "@pytest.mark.django_db(transaction=True)", + "@pytest.mark.django_db(transaction=True, serialized_rollback=False)", + "", + ], + ids=["marker", "marker-false", "no-marker"], + ) + @pytest.mark.parametrize("indirect", [False, True], ids=["direct", "dependency"]) + def test_serialized_rollback_fixture_with_marker( + self, + django_pytester: DjangoPytester, + marker: str, + indirect: bool, + ) -> None: + django_pytester.create_app_file( + """ + from django.db import migrations + + def load_data(apps, schema_editor): + Item = apps.get_model("app", "Item") + Item.objects.create(name="loaded-in-migration") + + class Migration(migrations.Migration): + dependencies = [("app", "0001_initial")] + operations = [migrations.RunPython(load_data)] + """, + "migrations/0002_data_migration.py", + ) + + fixture = "rollback_db" if indirect else "django_db_serialized_rollback" + django_pytester.create_test_module( + f""" + import pytest + from .app.models import Item + + @pytest.fixture + def rollback_db(django_db_serialized_rollback): + pass + + {marker} + def test_first(transactional_db, {fixture}): + assert Item.objects.filter(name="loaded-in-migration").exists() + Item.objects.create(name="test-only") + + {marker} + def test_second(transactional_db, {fixture}): + assert Item.objects.filter(name="loaded-in-migration").exists() + assert not Item.objects.filter(name="test-only").exists() + """ + ) + + result = django_pytester.runpytest_subprocess("-v") + result.assert_outcomes(passed=2) + @pytest.fixture def mydb(self, all_dbs: None) -> None: # noqa: ARG002 # This fixture must be able to access the database