Skip to main content

System Health and Validation

To validate project configuration and maintain integrity, you can use the system check framework to register custom validation logic that runs during development and deployment.

Registering a Custom System Check

You can create custom checks by defining a function that returns a list of messages and registering it with the @register decorator from django.core.checks.

from django.core.checks import Error, Tags, register

# Define a reusable error message
E001 = Error(
"The 'CUSTOM_STORAGE_PATH' setting is not defined.",
hint="Add CUSTOM_STORAGE_PATH to your settings.py to enable file uploads.",
id="myapp.E001",
)

@register(Tags.files)
def check_storage_configuration(app_configs, **kwargs):
from django.conf import settings
errors = []

if not hasattr(settings, "CUSTOM_STORAGE_PATH"):
errors.append(E001)

return errors

Using Check Message Levels

The framework provides several subclasses of CheckMessage to indicate the severity of the issue. These are available in django.core.checks:

  • Critical: For highly serious problems that prevent Django from running.
  • Error: For serious problems that likely prevent the application from functioning correctly.
  • Warning: For potential issues that might cause problems but don't prevent execution.
  • Info: For informative messages about the project state.
  • Debug: For low-level debugging information.

When creating a message, you can provide an obj to identify the source of the issue (such as a model or field) and a unique id for silencing.

from django.core.checks import Warning, Tags, register

@register(Tags.models)
def check_model_naming(app_configs, **kwargs):
from django.apps import apps
warnings = []

# app_configs is a list of AppConfig instances if filtered, or None for all apps
models = apps.get_models() if app_configs is None else [
m for ac in app_configs for m in ac.get_models()
]

for model in models:
if model.__name__.islower():
warnings.append(
Warning(
f"Model name '{model.__name__}' should be CamelCase.",
obj=model,
id="myapp.W001",
)
)
return warnings

Running Checks via Management Command

The check management command (implemented in django.core.management.commands.check.Command) is the primary interface for executing these validations.

# Run all registered checks
python manage.py check

# Run only checks associated with specific tags
python manage.py check --tag models --tag security

# Run checks for specific applications
python manage.py check myapp anotherapp

# List all available tags in the registry
python manage.py check --list-tags

Implementing Deployment Checks

Some checks are only relevant in a production environment. You can register these by setting deploy=True in the @register decorator. These checks are only executed when the --deploy flag is passed to the check command.

import os
from django.core.checks import Error, Tags, register

E001 = Error(
"DJANGO_ALLOW_ASYNC_UNSAFE is set in deployment.",
id="async.E001",
)

@register(Tags.async_support, deploy=True)
def check_async_unsafe(app_configs, **kwargs):
if os.environ.get("DJANGO_ALLOW_ASYNC_UNSAFE"):
return [E001]
return []

To run these:

python manage.py check --deploy

Validating Database Configurations

Checks tagged with Tags.database are handled specially by the CheckRegistry. They are not run by default unless you explicitly specify a database alias.

from django.core.checks import Error, Tags, register

@register(Tags.database)
def check_database_version(databases, **kwargs):
errors = []
if databases is None:
return []

for alias in databases:
# Perform database-specific version or configuration checks
pass
return errors

To execute database checks:

python manage.py check --database default

Troubleshooting and Gotchas

  • Missing **kwargs: Every check function must accept **kwargs. If it doesn't, CheckRegistry.register will raise a TypeError during registration.
  • Return Type: Check functions must return an iterable (typically a list) of CheckMessage instances. Returning a single message instead of a list will cause a TypeError when run_checks is called.
  • Silencing Checks: If a check produces a warning or error that you want to ignore, add its id to the SILENCED_SYSTEM_CHECKS list in your settings.py.
    # settings.py
    SILENCED_SYSTEM_CHECKS = ["myapp.W001"]
  • Database Exclusion: If your custom check uses Tags.database, it will be skipped during a standard python manage.py check unless the --database flag is used. Use a different tag if the check does not require an active database connection.