Рамка проверки системы

Фреймворк system check - это набор статических проверок для проверки проектов Django. Он обнаруживает распространенные проблемы и дает подсказки, как их устранить. Фреймворк является расширяемым, поэтому вы можете легко добавлять свои собственные проверки.

Проверки могут быть вызваны явно с помощью команды check. Проверки запускаются неявно перед большинством команд, включая runserver и migrate. По соображениям производительности, проверки не выполняются как часть стека WSGI, который используется при развертывании. Если вам нужно запустить системные проверки на сервере развертывания, запускайте их явно с помощью команды check.

Серьезные ошибки вообще не позволят выполнить команды Django (например, runserver). О незначительных проблемах сообщается в консоли. Если вы проверили причину предупреждения и готовы проигнорировать его, вы можете скрыть конкретные предупреждения с помощью параметра SILENCED_SYSTEM_CHECKS в файле настроек вашего проекта.

Полный список всех проверок, которые могут быть подняты Django, можно найти в System check reference.

Выписывайте собственные чеки

Фреймворк является гибким и позволяет писать функции, выполняющие любые другие виды проверок, которые вам могут потребоваться. Ниже приведен пример функции проверки заглушки:

from django.core.checks import Error, register


@register()
def example_check(app_configs, **kwargs):
    errors = []
    # ... your check logic here
    if check_failed:
        errors.append(
            Error(
                "an error",
                hint="A hint.",
                obj=checked_object,
                id="myapp.E001",
            )
        )
    return errors

Функция check должна принимать аргумент app_configs; этот аргумент представляет собой список приложений, которые должны быть проверены. Если None, то проверка должна быть выполнена для всех установленных приложений в проекте.

Проверка будет получать аргумент с ключевым словом databases. Это список псевдонимов баз данных, соединения которых могут быть использованы для проверки конфигурации уровня базы данных. Если databases равно None, то проверка не должна использовать никаких соединений с базой данных.

Аргумент **kwargs необходим для будущего расширения.

Сообщения

Функция должна возвращать список сообщений. Если в результате проверки проблем не обнаружено, функция проверки должна вернуть пустой список.

Предупреждения и ошибки, вызванные методом проверки, должны быть экземплярами CheckMessage. Экземпляр CheckMessage заключает в себе одну сообщаемую ошибку или предупреждение. Он также предоставляет контекст и подсказки, применимые к сообщению, и уникальный идентификатор, который используется для целей фильтрации.

Концепция очень похожа на сообщения message framework или logging framework. Сообщения помечаются символом level, указывающим на серьезность сообщения.

Существуют также ярлыки, облегчающие создание сообщений с общими уровнями. При использовании этих классов вы можете опустить аргумент level, поскольку он подразумевается именем класса.

Регистрация и маркировка чеков

Наконец, ваша функция проверки должна быть явно зарегистрирована в системном реестре проверок. Проверки должны быть зарегистрированы в файле, который загружается при загрузке вашего приложения; например, в методе AppConfig.ready().

register(*tags)(function)

Вы можете передать в register столько тегов, сколько хотите, чтобы пометить вашу проверку. Пометка проверок тегами полезна, так как позволяет запускать только определенную группу проверок. Например, чтобы зарегистрировать проверку совместимости, вы должны сделать следующий вызов:

from django.core.checks import register, Tags


@register(Tags.compatibility)
def my_check(app_configs, **kwargs):
    # ... perform compatibility checks and collect errors
    return errors

Вы можете зарегистрировать «проверки развертывания», которые относятся только к производственному файлу настроек, следующим образом:

@register(Tags.security, deploy=True)
def my_check(app_configs, **kwargs):
    ...

Эти проверки будут выполняться только при использовании опции check --deploy.

Вы также можете использовать register как функцию, а не декоратор, передав вызываемый объект (обычно функцию) в качестве первого аргумента в register.

Код ниже эквивалентен коду выше:

def my_check(app_configs, **kwargs):
    ...


register(my_check, Tags.security, deploy=True)

Проверки полей, моделей, менеджеров и баз данных

В некоторых случаях вам не нужно регистрировать свою функцию проверки - вы можете использовать уже существующую регистрацию.

Поля, модели, менеджеры моделей и бэкенды баз данных реализуют метод check(), который уже зарегистрирован в системе проверок. Если вы хотите добавить дополнительные проверки, вы можете расширить реализацию базового класса, выполнить любые дополнительные проверки, которые вам нужны, и добавить любые сообщения к тем, которые генерируются базовым классом. Рекомендуется делегировать каждую проверку отдельным методам.

Рассмотрим пример, в котором вы реализуете пользовательское поле с именем RangedIntegerField. Это поле добавляет аргументы min и max в конструктор IntegerField. Возможно, вы захотите добавить проверку, чтобы убедиться, что пользователи предоставляют минимальное значение, которое меньше или равно максимальному значению. Следующий фрагмент кода показывает, как можно реализовать такую проверку:

from django.core import checks
from django.db import models


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):
        # Call the superclass
        errors = super().check(**kwargs)

        # Do some custom checks and add messages to `errors`:
        errors.extend(self._check_min_max_values(**kwargs))

        # Return all errors and warnings
        return errors

    def _check_min_max_values(self, **kwargs):
        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",
                )
            ]
        # When no error, return an empty list
        return []

Если бы вы хотели добавить проверки в менеджер моделей, вы бы использовали тот же подход в своем подклассе Manager.

Если вы хотите добавить проверку в класс модели, подход почти такой же: единственное различие в том, что проверка является методом класса, а не методом экземпляра:

class MyModel(models.Model):
    @classmethod
    def check(cls, **kwargs):
        errors = super().check(**kwargs)
        # ... your own checks ...
        return errors

Написание тестов

Сообщения сопоставимы. Это позволяет легко писать тесты:

from django.core.checks import Error

errors = checked_object.check()
expected_errors = [
    Error(
        "an error",
        hint="A hint.",
        obj=checked_object,
        id="myapp.E001",
    )
]
self.assertEqual(errors, expected_errors)

Написание интеграционных тестов

Учитывая необходимость регистрации определенных проверок при загрузке приложения, может быть полезно протестировать их интеграцию в рамках системных проверок. Это можно сделать с помощью функции call_command().

Например, этот тест демонстрирует, что параметр SITE_ID должен быть целым числом, встроенным check from the sites framework:

from django.core.management import call_command
from django.core.management.base import SystemCheckError
from django.test import SimpleTestCase, modify_settings, override_settings


class SystemCheckIntegrationTest(SimpleTestCase):
    @override_settings(SITE_ID="non_integer")
    @modify_settings(INSTALLED_APPS={"prepend": "django.contrib.sites"})
    def test_non_integer_site_id(self):
        message = "(sites.E101) The SITE_ID setting must be an integer."
        with self.assertRaisesMessage(SystemCheckError, message):
            call_command("check")

Рассмотрим следующую проверку, которая выдает предупреждение при развертывании, если пользовательский параметр с именем ENABLE_ANALYTICS не установлен в значение True:

from django.conf import settings
from django.core.checks import Warning, register


@register("myapp", deploy=True)
def check_enable_analytics_is_true_on_deploy(app_configs, **kwargs):
    errors = []
    if getattr(settings, "ENABLE_ANALYTICS", None) is not True:
        errors.append(
            Warning(
                "The ENABLE_ANALYTICS setting should be set to True in deployment.",
                id="myapp.W001",
            )
        )
    return errors

Учитывая, что эта проверка не приведет к появлению SystemCheckError, наличие предупреждения в выводе stderr можно утверждать следующим образом:

from io import StringIO

from django.core.management import call_command
from django.test import SimpleTestCase, override_settings


class EnableAnalyticsDeploymentCheckTest(SimpleTestCase):
    @override_settings(ENABLE_ANALYTICS=None)
    def test_when_set_to_none(self):
        stderr = StringIO()
        call_command("check", "-t", "myapp", "--deploy", stderr=stderr)
        message = (
            "(myapp.W001) The ENABLE_ANALYTICS setting should be set "
            "to True in deployment."
        )
        self.assertIn(message, stderr.getvalue())
Вернуться на верх