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.registerwill raise aTypeErrorduring registration. - Return Type: Check functions must return an iterable (typically a
list) ofCheckMessageinstances. Returning a single message instead of a list will cause aTypeErrorwhenrun_checksis called. - Silencing Checks: If a check produces a warning or error that you want to ignore, add its
idto theSILENCED_SYSTEM_CHECKSlist in yoursettings.py.# settings.py
SILENCED_SYSTEM_CHECKS = ["myapp.W001"] - Database Exclusion: If your custom check uses
Tags.database, it will be skipped during a standardpython manage.py checkunless the--databaseflag is used. Use a different tag if the check does not require an active database connection.