skip to content

What is Django's system check framework, and when do its checks run without anyone calling manage.py check?

level: juniorimportance: should knowfreq 45%

answer

  1. static validation of the project
  2. before most management commands
  3. runserver on every reload
  4. absent from the request path
  5. tags choose which checks run

basics

~20 s

Django's system check framework (django.core.checks) is a registry of static validations over models, settings, URLs and templates. Besides manage.py check, it runs before most management commands, including runserver and migrate, but never inside the deployed request path.

solid answer

~40 s

The framework is a registry of check functions that return `CheckMessage` objects (`Warning`, `Error` and so on); Django ships checks tagged `models`, `urls`, `templates`, `security`, `caches`, `database` and more. Besides the explicit `manage.py check`, `BaseCommand.execute()` runs them before any command whose `requires_system_checks` is `"__all__"` (the default) or a list of tags, so `makemigrations`, `migrate` and most custom commands are covered; `runserver` runs them itself on start and after every autoreload, `migrate` adds the `database`-tagged checks for its target alias, and the test runner calls `check` once the test databases exist. An `Error` or `Critical` stops the command with a `SystemCheckError`; warnings are only printed. Checks are **not** part of the WSGI or ASGI request path, so production never runs them unless you call `check` yourself.

code

bash · 5 lines
bash
python manage.py check                        # all non-deploy, non-database checks
python manage.py check shop --tag models --tag urls
python manage.py check --database default     # add database-tagged checks
python manage.py check --list-tags --deploy
python manage.py makemigrations --skip-checks  # skip the implicit pre-command run

go deeper

for a junior

Recall that manage.py check validates the project statically, that runserver and migrate run the checks automatically, and that errors stop the command while warnings are printed.

for a middle

Explain requires_system_checks, runserver's run on each reload, migrate enabling database checks, call_command skipping checks, and the absence of checks in the request path.

for a senior

Show that you run check --deploy against production settings in the release pipeline, because the deployed server never runs checks on its own.

for a principal

Treat the check registry as a place to encode project-wide invariants cheaply, and decide which ones must block releases versus merely warn developers.

## What the framework is The **system check framework** lives in `django.core.checks`. It is a registry of plain functions, each of which inspects some part of the project and returns a list of **messages** (`CheckMessage` instances such as `Warning` or `Error`). It is *static*: it validates configuration and code — model definitions, field options, URL patterns, template settings, cache configuration, security settings — not the rows in your database. Django registers its own checks when `django.core.checks` is imported, and contrib apps add more (`admin`, `auth`, `staticfiles`, `sites`, `postgres`…). Your apps can register their own the same way. ## What ships in the box Checks are labelled with **tags**, and the tag is how you select them: | Tag | What it looks at | |---|---| | `models` | Model, field and manager definitions | | `urls` | URLconf patterns and settings that point to URLconfs | | `templates` | The `TEMPLATES` setting | | `security` | Security settings; most are deployment-only | | `caches`, `files`, `mail`, `translation` | The matching settings | | `compatibility` | Things likely to break on an upgrade | | `database` | Database configuration; needs a live connection | | `admin`, `staticfiles`, `sites` | The contrib apps of the same names | `manage.py check --list-tags` prints the tags actually registered in your project; add `--deploy` to include deployment-only tags. ## When checks run implicitly - **Most management commands.** `BaseCommand.execute()` runs checks before `handle()` whenever the command's `requires_system_checks` attribute is `"__all__"` (the default) or a list of tags. `makemigrations`, `migrate` and custom commands inherit this; `--skip-checks` turns it off for one run. - **`runserver`.** It opts out of the generic pre-check and runs the full set itself, on start and again after every autoreload, printing `System check identified no issues (0 silenced).` when all is well. - **`migrate`.** It passes its `--database` alias to the checks, which is what enables the **`database`-tagged** checks. Other commands leave them out. - **The test runner.** After creating the test databases it calls `check` with those aliases, so a broken check fails the test run early. ## What does not run them - **The deployed request path.** For performance reasons, checks are not part of the WSGI or ASGI stack. A production server can import and run a project that `check` would reject. - **`call_command()`** from code, which passes `skip_checks=True` unless told otherwise. - **Commands that opt out**, such as `shell`, `test` and `check` itself, whose `requires_system_checks` is an empty list; `check` of course runs them as its whole job. ## What happens with the results 1. `Error` and `Critical` messages are **serious**: the command raises `SystemCheckError` (a `CommandError`), prints every issue, and exits with status 1 without running `handle()`. 2. `Warning`, `Info` and `Debug` messages are printed and the command carries on. 3. Any message whose id is in `SILENCED_SYSTEM_CHECKS` is hidden and counted in the `(N silenced)` summary. ## Common misunderstandings - **"Checks validate my data."** They validate definitions and settings. A model check tells you a `ForeignKey` is misconfigured; it never looks at the rows. - **"Warnings block the server."** Only `Error` and `Critical` stop a command. A project can run for years with warnings nobody reads, which is why pipelines raise the fail level. - **"Production is protected because runserver checked it."** The development server checked the development settings. The production process imports different settings and runs no checks at all. - **"Checks are expensive, so skip them."** For static checks the cost is small; `--skip-checks` exists for special cases such as scripted runs where checks already passed, not as a default. ## Running a subset by hand - `manage.py check shop orders` limits app-scoped checks to those apps. - `manage.py check --tag models --tag urls` runs only checks with those tags. - `manage.py check --database default` also runs the `database` checks against that alias. - `manage.py check --deploy` adds the deployment checks; `--fail-level` decides which level makes the command exit non-zero.

  • Why doesn't Django run the system checks when a production WSGI server loads the project?
    Checks inspect every model, URL pattern and setting, which is too costly to repeat on every process start or request, so the docs keep them out of the WSGI stack. The consequence is that you run them deliberately: `manage.py check --deploy` against production settings in the release pipeline, so a broken configuration fails the release rather than the first user request.
  • How can a custom management command choose which checks run before its handle() method?
    Set `requires_system_checks` to a list of tags, such as `[Tags.models]`, or to `[]` to run none; the default `"__all__"` runs every non-database check. Since Django 5.2 you can also override `get_check_kwargs()` to change the arguments passed to `check()`, for example to add a `databases` list and opt into database checks.

The checks are a pre-flight checklist: the crew runs it before every departure (each management command), not once per passenger (each request) during the flight.

saying these in an interview costs you the question

  • System checks run on every HTTP request in production
  • A Warning from a system check stops runserver from starting
  • Only the check command ever runs system checks
  • Database-tagged checks run before every management command
  • System checks validate the data stored in your tables