With pytest-django, why does a plain test function that queries a Django model fail with 'Database access not allowed', and how do you fix it?
answer
- blocked by default
- a marker or a fixture
- opt in per test
- rolled back like TestCase
basics
~10 spytest-django blocks database access unless a test opts in, so an unmarked query raises RuntimeError. Add @pytest.mark.django_db or request the db fixture; the test then runs in a transaction that is rolled back afterwards.
solid answer
~40 spytest-django deliberately makes the database unreachable by default: it patches Django's connection setup so any query raises `RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.` A test opts in either with `@pytest.mark.django_db` (on the function, a class, or a module through `pytestmark`) or by requesting the `db` fixture, directly or through another fixture such as `admin_user` or `django_user_model`. The first such test triggers the session-scoped `django_db_setup`, which creates and migrates the test database. Each opted-in test then runs inside a transaction that is rolled back at the end, the same isolation Django's `TestCase` gives. Tests that never opt in stay database-free and fast, which is the point of the default.
code
python · 13 linesimport pytest
from orders.models import Order
@pytest.fixture
def pending_order(db): # requesting db opts every user in
return Order.objects.create(total_cents=500)
def test_cancel(pending_order):
pending_order.cancel()
assert pending_order.status == "cancelled"go deeper
Remember that pytest-django blocks the database until a test uses the django_db marker or the db fixture.
Explain how opting in triggers django_db_setup once per session and wraps each test in a rolled-back transaction, and which fixtures request db for you.
Use the block as a design tool: keep most tests database-free, put db on data fixtures, and catch accidental ORM use in unit tests.
Set suite conventions for marking database tests so the split between fast unit tests and database tests stays visible and intentional.
## Why the default is "no database" Under Django's own runner, any `TestCase` or `TransactionTestCase` can touch the database and `SimpleTestCase` refuses queries. pytest has no base classes, so **pytest-django** needs another way to know which tests need a database. Its answer is to **block database access by default** and make each test opt in. The block is implemented by patching the method Django uses to open a connection. Any query from a test, or from a fixture it uses, that has not opted in raises: ```text RuntimeError: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it. ``` The benefits are deliberate: - **Speed.** A suite of pure-logic tests never creates a database at all. - **Explicitness.** Reading a test tells you whether it touches the database. - **Catching surprises.** A "unit" test that quietly queries the ORM fails loudly instead of silently becoming an integration test. ## The two ways to opt in 1. **The marker.** `@pytest.mark.django_db` on a test function or a test class, or `pytestmark = pytest.mark.django_db` at module level. 2. **The `db` fixture.** Request it directly, or indirectly through a fixture that depends on it. `django_user_model`, `admin_user` and `admin_client` all request `db`, so a test using them has database access without a marker. ```python import pytest from orders.models import Order @pytest.mark.django_db def test_new_order_is_pending(): order = Order.objects.create(total_cents=500) assert order.status == "pending" def test_order_count(db): assert Order.objects.count() == 0 ``` ## What happens once a test opts in - The session-scoped **`django_db_setup`** fixture runs on first use: it creates the test database (the usual `test_` prefix), applies migrations, and keeps it for the rest of the session. - The test itself is wrapped in a **transaction that is rolled back** when it ends, which is the isolation Django's `TestCase` provides. Rows a test creates are invisible to the next test. - For real commits, use `@pytest.mark.django_db(transaction=True)` or the `transactional_db` fixture, which behave like `TransactionTestCase` and are slower. | Test declares | Database access | Isolation | |---|---|---| | nothing | blocked, `RuntimeError` on query | n/a | | `@pytest.mark.django_db` or `db` | allowed | per-test transaction rolled back | | `django_db(transaction=True)` or `transactional_db` | allowed | real commits, tables flushed afterwards | ## Common causes of the error in real suites - A **fixture** creates model rows but neither it nor the test requests `db`. Add `db` to the fixture's parameters so every user of the fixture opts in. - A test uses the **`client` fixture** and the view queries the database. `client` itself does not request `db`, unlike `admin_client`, so the test still needs the marker. - Code runs a query at **import time** or in a module-level fixture; move it into a function-scoped fixture that requests `db`. - A module-level `pytestmark` was forgotten when tests were moved to a new file. Existing Django `TestCase` classes collected by pytest are not affected: pytest-django recognises them and unblocks the database for them automatically, which is what lets a suite migrate to plain functions gradually. ## Fixtures that opt in for you Several built-in fixtures request `db` themselves, which is why some tests work without a marker and others do not: | Fixture | Requests `db`? | Why | |---|---|---| | `client`, `async_client` | no | just a `django.test.Client` / `AsyncClient` | | `rf`, `async_rf` | no | builds requests only | | `settings` | no | overrides settings only | | `django_user_model` | yes | the user model is usually queried next | | `admin_user`, `admin_client` | yes | they create or fetch a superuser | | `transactional_db`, `live_server` | yes, transactional | real commits are needed | The practical rule: put `db` in the parameter list of every custom fixture that creates model rows, and mark tests that query through views or services directly. ## How the database gets created `django_db_setup` wraps Django's own test-database setup, so the database is named and migrated exactly as `manage.py test` would do it. pytest-django adds its own switches: `--reuse-db` keeps the database between runs, `--create-db` forces recreation, and `--no-migrations` builds tables straight from the models. Since pytest-django 4.11 the plugin also skips database setup entirely when no collected test asks for a database.
- A test uses pytest-django's client fixture to GET a page whose view queries the database; why does it still fail with 'Database access not allowed'?The `client` fixture just returns a `django.test.Client` and does not request `db`, so the view's query is blocked. Add `@pytest.mark.django_db` or request `db`. By contrast `admin_client` depends on `admin_user`, which requests `db`, so tests using it already have access.
- How do you enable database access for every test in one module with pytest-django?Set `pytestmark = pytest.mark.django_db` at module level; pytest applies the marker to every test in the file. Use `pytestmark = pytest.mark.django_db(transaction=True)` only if the whole module genuinely needs real commits, since that is slower.
saying these in an interview costs you the question
- Believing pytest-django gives every test database access by default
- Thinking the django_db marker commits data that later tests can see
- Fixing the error by pointing tests at the development database
- Assuming the client fixture already requests the db fixture
- Saying Django TestCase classes also need the marker under pytest