Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ Compatibility
* Added official support for Django 5.2 (`PR #1179 <https://github.com/pytest-dev/pytest-django/pull/1179>`__).
* Dropped testing on MySQL’s MyISAM storage engine (`PR #1180 <https://github.com/pytest-dev/pytest-django/pull/1180>`__).

Bugfixes
^^^^^^^^

* Stopped setting up and serializing databases on test session setup when not needed (the database is not requested / ``serialized_rollback`` is not used).
On test databases with large amounts of pre-seeded data, this may remove a delay of a few seconds when running ``pytest --reuse-db``.

The determination of which databases to setup is done by static inspection of the test suite.
Using pytest's dynamic features to request db access, such as :meth:`request.getfixturevalue("db") <pytest.FixtureRequest.getfixturevalue>`, may throw off this analysis.
If you start seeing ``DatabaseOperationForbidden`` or "unable to open database" errors, this is likely the cause.
To fix this, decorate at least one test with the :func:`django_db <pytest.mark.django_db>` marker with appropriate ``databases`` and ``serialized_rollback`` settings.

v4.10.0 (2025-02-10)
--------------------

Expand Down
56 changes: 56 additions & 0 deletions pytest_django/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from functools import partial
from typing import (
TYPE_CHECKING,
AbstractSet,
Any,
Callable,
ContextManager,
Expand All @@ -16,6 +17,7 @@
Literal,
Optional,
Protocol,
Sequence,
Tuple,
Union,
)
Expand Down Expand Up @@ -119,6 +121,56 @@ def django_db_createdb(request: pytest.FixtureRequest) -> bool:
return create_db


def _get_databases_for_test(test: pytest.Item) -> tuple[Iterable[str], bool]:
"""Get the database aliases that need to be setup for a test, and whether
they need to be serialized."""
from django.db import DEFAULT_DB_ALIAS, connections
from django.test import TransactionTestCase

test_cls = getattr(test, "cls", None)
if test_cls and issubclass(test_cls, TransactionTestCase):
serialized_rollback = getattr(test, "serialized_rollback", False)
databases = getattr(test, "databases", None)
else:
fixtures = getattr(test, "fixturenames", ())
marker_db = test.get_closest_marker("django_db")
if marker_db:
(
transaction,
reset_sequences,
databases,
serialized_rollback,
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
databases = None
else:
return (), False
if databases is None:
return (DEFAULT_DB_ALIAS,), serialized_rollback
elif databases == "__all__":
return connections, serialized_rollback
else:
return databases, serialized_rollback


def _get_databases_for_setup(
items: Sequence[pytest.Item],
) -> tuple[AbstractSet[str], AbstractSet[str]]:
"""Get the database aliases that need to be setup, and the subset that needs
to be serialized."""
# Code derived from django.test.utils.DiscoverRunner.get_databases().
aliases: set[str] = set()
serialized_aliases: set[str] = set()
for test in items:
databases, serialized_rollback = _get_databases_for_test(test)
aliases.update(databases)
if serialized_rollback:
serialized_aliases.update(databases)
return aliases, serialized_aliases


@pytest.fixture(scope="session")
def django_db_setup(
request: pytest.FixtureRequest,
Expand All @@ -140,10 +192,14 @@ def django_db_setup(
if django_db_keepdb and not django_db_createdb:
setup_databases_args["keepdb"] = True

aliases, serialized_aliases = _get_databases_for_setup(request.session.items)

with django_db_blocker.unblock():
db_cfg = setup_databases(
verbosity=request.config.option.verbose,
interactive=False,
aliases=aliases,
serialized_aliases=serialized_aliases,
**setup_databases_args,
)

Expand Down