Django System Check Framework: A Complete Guide to Writing, Registering, Running, and Testing System Checks

This article explains Django’s System Check Framework—a powerful mechanism for detecting configuration issues, validating project structure, and ensuring code quality. It covers how checks are executed, how to write custom checks, how messages work, how to register and tag checks, how to extend checks for fields and models, and how to write both unit and integration tests for system checks.

Django system check, custom checksCheckMessage, register check, Tags, ErrorWarning, check command, Django validation

~3 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

Related Articles

Django Tasks Framework: A Complete Guide to Background Task Execution in Django 6.0

Django 6.0 introduces the Tasks framework, a built‑in system for defining and queuing background work outside the request–response cycle. This article explains how Tasks work, how to configure backends, how to define and enqueue tasks, how context works, and how to integrate third‑party worker systems for production environments.

Continue

Asynchronous Support in Django: A Complete Guide to Async Views, ORM, Middleware, Performance, and Safety

This article explains Django’s asynchronous (async) capabilities, including async views, ASGI support, middleware behavior, async ORM features, performance considerations, handling disconnects, and Django’s async safety protections. It also covers how to use sync_to_async(), async ORM methods, and how to safely run synchronous code in async environments.

Continue

Django Signals: A Complete Guide to Listening, Connecting, Sending, and Managing Application Events

This article provides a comprehensive explanation of Django’s signal system—an event‑driven mechanism that allows decoupled applications to react to actions occurring elsewhere in the framework. It covers how to define receivers, connect signals, use decorators, handle specific senders, organize signal code, and follow best practices to avoid complexity.

Continue

Understanding Django Settings: Configuration, Environment Management, and Best Practices

This article provides a complete overview of Django’s settings system. It explains how settings files work, how to designate a settings module, how to use settings in your code, how to configure Django manually, how to secure sensitive settings, and how to work with custom default settings. It also covers the role of django.setup() for standalone scripts.

Continue

Serializing and Deserializing Django Objects: A Complete Guide to Django’s Serialization Framework

This article explains Django’s serialization framework, including how to serialize and deserialize model instances, work with subsets of fields, handle inherited models, use different serialization formats (JSON, XML, YAML, JSONL), and understand how relational fields are represented. It also covers DeserializedObject behavior and common pitfalls.

Continue

Performance and Optimization in Django: A Complete Guide to Benchmarking, Caching, Laziness, and Efficient Code Design

This article provides a comprehensive overview of performance and optimization techniques in Django. It covers benchmarking, profiling tools, choosing the right abstraction level, caching strategies, understanding laziness, and how Django’s QuerySets benefit from delayed evaluation.

Continue