How does pytest-django decide which Django settings module to load, and what is the precedence between --ds, DJANGO_SETTINGS_MODULE and pytest.ini?
answer
- command line, environment, config file
- addopts to force the config
- manage.py and the Python path
- fixtures skip without settings
basics
~10 spytest-django takes the settings module from --ds first, then the DJANGO_SETTINGS_MODULE environment variable, then the DJANGO_SETTINGS_MODULE key in pytest's config file. Putting --ds in addopts makes the file win.
solid answer
~40 spytest-django resolves the settings module in a fixed order: the `--ds` command-line option, then the `DJANGO_SETTINGS_MODULE` environment variable, then a `DJANGO_SETTINGS_MODULE` key in the pytest configuration file (`pytest.ini`, `tox.ini` or `pyproject.toml`). The test header shows which source won, for example `settings: config.settings.test (from ini)`. Because the environment beats the ini file, a developer shell that exports the local settings module silently overrides the project's test settings; writing `addopts = --ds=config.settings.test` in the config makes the file's choice highest priority. Separately, `django_find_project` (on by default) looks for `manage.py` and adds its directory to the Python path so the module can be imported. If no settings are found, Django fixtures such as `client`, `rf` and `settings` skip with "no Django settings" rather than erroring.
go deeper
Know the three places a settings module can come from: --ds, the environment variable and the pytest config file.
Explain the precedence order, the addopts trick, and how django_find_project makes the module importable.
Diagnose wrong-settings runs from the session header and design configuration that developer shells and CI cannot silently override.
Standardise test settings across services so every pipeline and laptop runs the same configuration.
## Why it matters pytest knows nothing about Django until pytest-django configures it, and everything depends on **which settings module** it loads: the database the test database is derived from, the password hashers, `INSTALLED_APPS`, email backends. Loading the development settings instead of the test settings is a classic source of slow or confusing test runs. ## The precedence order pytest-django checks three sources, highest first: 1. **`--ds`** on the command line: `pytest --ds=config.settings.test`. 2. The **`DJANGO_SETTINGS_MODULE` environment variable**. 3. A **`DJANGO_SETTINGS_MODULE` key** in pytest's configuration file. ```ini # pytest.ini [pytest] DJANGO_SETTINGS_MODULE = config.settings.test addopts = --ds=config.settings.test ``` The second line is the documented trick for making the configuration file authoritative: options in `addopts` count as command-line options, so they beat the environment variable. | Source | Example | Beats | |---|---|---| | `--ds` (including via `addopts`) | `--ds=config.settings.test` | environment and ini key | | environment variable | `export DJANGO_SETTINGS_MODULE=config.settings.dev` | ini key | | ini key | `DJANGO_SETTINGS_MODULE = config.settings.test` | nothing | pytest-django prints the winner in the session header, for example `settings: config.settings.test (from ini)`, which is the first thing to check when tests behave as if they used the wrong settings. ## Making the module importable A dotted module path is useless if Python cannot import it: - The **`django_find_project`** ini option is **true by default**. pytest-django searches for a `manage.py` near the test paths and adds that directory to `sys.path`, reporting that it did so. - Projects with a non-standard layout can set `django_find_project = false` and manage the Python path themselves, for example with pytest's own `pythonpath` setting. - If the module cannot be imported, pytest-django stops with a message explaining the settings value and where it came from. ## What happens without settings When no settings module is configured at all, pytest-django stays out of the way for tests that do not need Django: Django-specific fixtures such as `client`, `rf` and `settings` call a helper that **skips the test with "no Django settings"**. That avoids crashing pure-Python test suites that happen to have pytest-django installed, but it can also hide a misconfiguration, so a CI job with unexpectedly many skips deserves a look. ## Practical setup - Keep a dedicated **test settings module** (fast password hasher, in-memory email, test-friendly storage). - Name it in the **configuration file**, and add `--ds` to `addopts` if developers export other settings in their shells. - For a one-off run against different settings, pass `--ds` explicitly on the command line; it overrides everything else. - There is a parallel `--dc` option and `DJANGO_CONFIGURATION` key for projects that use the third-party django-configurations package. ## A worked diagnosis A developer reports that tests take ten times longer on their laptop than on CI and that the test database appears on the shared development database server. The checklist: 1. Look at the session header. It reads `settings: config.settings.dev (from env)`. 2. Their shell profile exports `DJANGO_SETTINGS_MODULE=config.settings.dev` for `runserver`. 3. The environment beats the ini key, so the development settings were loaded: the default slow password hasher, development-only apps in `INSTALLED_APPS`, and a `DATABASES` entry pointing at the shared server. 4. Fix locally by unsetting the variable for test runs; fix for everyone by adding `addopts = --ds=config.settings.test` so the configuration file wins. The same header line is worth adding to CI logs review: it proves which settings a pipeline actually used. ## Related switches that often come up - **`--ds` versus `--dc`.** `--ds` names the settings module; `--dc` names a configuration class for projects using django-configurations, with the same option, environment, ini precedence. - **`--reuse-db` and `--create-db`.** Not about which settings load, but about what happens to the test database those settings describe; they are frequently added to `addopts` next to `--ds`. - **`--no-migrations`.** Builds the test database from the models instead of running migrations, trading migration coverage for speed. Knowing that all of these can live in `addopts` is what makes a project's pytest configuration a single, reviewable source of truth for how tests run.
- Tests suddenly run against your local development settings even though pytest.ini names the test settings; what is the likely cause?The shell exports `DJANGO_SETTINGS_MODULE`, and the environment variable outranks the ini key. The session header will say `(from env)`. Unset the variable, or put `addopts = --ds=config.settings.test` in the configuration so the command-line source wins.
saying these in an interview costs you the question
- Believing the pytest.ini key always wins over the environment variable
- Thinking pytest-django reads the settings module from manage.py automatically
- Assuming a missing settings module makes every test fail immediately
- Saying --ds only works when no environment variable is set