~5 min read • Updated Mar 15, 2026
What Is the Django System Check Framework?
The System Check Framework is a collection of static validation routines that analyze your Django project for common problems. Checks run automatically before most management commands such as runserver and migrate, and can also be triggered manually using:
python manage.py check
In production environments, checks are not executed automatically for performance reasons. If needed, you can run them explicitly on the server.
Critical errors prevent Django from running, while warnings are printed to the console. You can silence specific warnings using SILENCED_SYSTEM_CHECKS.
Writing Custom Checks
A custom check is simply a function that inspects part of your project and returns a list of messages.
Example Check Function
from django.core.checks import Error, register
@register()
def example_check(app_configs, **kwargs):
errors = []
if check_failed:
errors.append(
Error(
"an error",
hint="A hint.",
obj=checked_object,
id="myapp.E001",
)
)
return errors
Function Parameters
- app_configs: list of apps to inspect (or None for all apps)
- databases: list of database aliases allowed for inspection
- **kwargs: reserved for future expansion
Messages
A check must return a list of CheckMessage instances. If no issues are found, return an empty list.
Message levels include:
- Debug
- Info
- Warning
- Error
- Critical
Convenience classes like Error and Warning automatically set the level.
Registering and Tagging Checks
Checks must be registered using the @register() decorator or by calling register() directly.
Tagging Checks
from django.core.checks import register, Tags
@register(Tags.compatibility)
def my_check(app_configs, **kwargs):
return errors
For deployment‑specific checks:
@register(Tags.security, deploy=True)
def my_check(app_configs, **kwargs):
...
These checks run only when using:
python manage.py check --deploy
Checks for Fields, Models, Managers, and Backends
Many Django components already implement a check() method. You can extend these to add custom validation.
Example: Custom Field Check
class RangedIntegerField(models.IntegerField):
def __init__(self, min=None, max=None, **kwargs):
super().__init__(**kwargs)
self.min = min
self.max = max
def check(self, **kwargs):
errors = super().check(**kwargs)
errors.extend(self._check_min_max_values())
return errors
def _check_min_max_values(self):
if self.min is not None and self.max is not None and self.min > self.max:
return [
checks.Error(
"min greater than max.",
hint="Decrease min or increase max.",
obj=self,
id="myapp.E001",
)
]
return []
Model-Level Checks
class MyModel(models.Model):
@classmethod
def check(cls, **kwargs):
errors = super().check(**kwargs)
# custom checks...
return errors
Writing Unit Tests for Checks
Check messages are comparable, making them easy to test:
from django.core.checks import Error
errors = checked_object.check()
expected = [
Error("an error", hint="A hint.", obj=checked_object, id="myapp.E001")
]
self.assertEqual(errors, expected)
Integration Testing with call_command()
To test how checks behave when executed through the management command:
from django.core.management import call_command
from django.core.management.base import SystemCheckError
with self.assertRaisesMessage(SystemCheckError, "(sites.E101) ..."):
call_command("check")
Example: Testing a Deployment Warning
from io import StringIO
stderr = StringIO()
call_command("check", "-t", "myapp", "--deploy", stderr=stderr)
self.assertIn("(myapp.W001)", stderr.getvalue())
Conclusion
Django’s System Check Framework is a powerful tool for ensuring project stability, correctness, and security. By writing custom checks, tagging them appropriately, extending checks on fields and models, and validating them with unit and integration tests, you can maintain a clean and reliable Django codebase—especially in large or production‑grade applications.
Written & researched by Dr. Shahin Siami