Marco de sistema de comprobación

El marco de sistema de comprobación es un conjunto de comprobaciones estáticas para validar proyectos Django. Detecta problemas comunes y proporciona pistas sobre cómo solucionarlos. El marco es extensible, por lo que puedes agregar fácilmente tus propias comprobaciones.

Las comprobaciones se pueden desencadenar explícitamente mediante el comando check. Las comprobaciones se desencadenan de manera implícita antes de la mayoría de los comandos, incluyendo runserver y migrate. Por razones de rendimiento, las comprobaciones no se ejecutan como parte del stack WSGI que se utiliza en la implementación. Si necesitas ejecutar comprobaciones de sistema en tu servidor de implementación, desencadena explícitamente usando el comando check.

Los errores graves impedirán que los comandos Django (como runserver) funcionen en absoluto. Los problemas menores se informan a la consola. Si has inspeccionado la causa de una advertencia y estás contento con ignorarla, puedes ocultar advertencias específicas utilizando el SILENCED_SYSTEM_CHECKS configuración en tu archivo de configuración del proyecto.

A continuación, te proporciono las traducciones de los textos originales manteniendo todas sus etiquetas intactas.

Escribiendo tus propios controles

El marco es flexible y permite escribir funciones que realicen cualquier otro tipo de control que pueda requerirse. A continuación, se muestra un ejemplo de función de control vacía:

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

La función de control debe aceptar el argumento app_configs; este argumento es la lista de aplicaciones que deben ser inspeccionadas. Si None, el control debe ejecutarse en todas las aplicaciones instaladas del proyecto.

El control recibirá el argumento clave databases. Este es una lista de alias de bases de datos cuyas conexiones pueden utilizarse para inspeccionar la configuración a nivel de base de datos. Si databases es None, el control no debe utilizar ninguna conexión a base de datos.

El argumento **kwargs es requerido para futuras expansiones.

Mensajes

La función debe devolver una lista de mensajes. Si no se encuentran problemas como resultado del control, la función de control debe devolver una lista vacía.

Las advertencias y errores levantados por el método de control deben ser instancias de CheckMessage. Una instancia de CheckMessage encapsula un error o advertencia reportable individual. También proporciona contexto e indicaciones aplicables al mensaje, así como un identificador único que se utiliza para fines de filtrado.

El concepto es muy similar a los mensajes del marco de mensajes o el marco de registro. Los mensajes están etiquetados con un level indicando la severidad del mensaje.

Hay también atajos para hacer que crear mensajes con niveles comunes sea más fácil. Al utilizar estas clases puedes omitir el argumento level porque está implícito en el nombre de la clase.

  • Depuración

  • Info

  • Advertencia

  • Error

  • Crítico

Registro y etiquetado de controles

Finalmente, tu función de comprobación debe registrarse explícitamente con el registro de comprobaciones del sistema. Las comprobaciones deben registrarse en un archivo que se carga cuando se carga tu aplicación; por ejemplo, en el método AppConfig.ready().

register(*tags)(function)

Puedes pasar tantos tags como desees a register para etiquetar tu verificación. Etiquetar las verificaciones es útil ya que te permite ejecutar solo un grupo específico de verificaciones. Por ejemplo, para registrar una verificación de compatibilidad, harías la siguiente llamada:

from django.core.checks import register, Tags


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

Puedes registrar «comprobaciones de despliegue» que solo son relevantes para un archivo de configuración de producción como esto:

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

Estos controles solo se ejecutarán si se utiliza la opción check --deploy.

También puedes utilizar register como una función en lugar de un decorador pasando un objeto callable (generalmente una función) como primer argumento a register.

El código a continuación es equivalente al código anterior:

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


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

Verificación de campos, modelos, administradores de modelos, motores de plantillas y bases de datos

En algunos casos, no necesitarás registrar tu función de verificación – puedes aprovechar una existente.

Los campos, los modelos, los administradores de modelos, los motores de plantillas y las bases de datos implementan un método check() que ya está registrado con el marco de verificación. Si deseas agregar comprobaciones adicionales, puedes extender la implementación en la clase base, realizar cualquier comprobación adicional que necesites y anexar cualquier mensaje a los generados por la clase base. Se recomienda delegar cada comprobación a métodos separados.

Considera un ejemplo donde estás implementando un campo personalizado llamado RangedIntegerField. Este campo agrega argumentos min y max al constructor del campo IntegerField. Puede que desees agregar una comprobación para asegurarte de que los usuarios proporcionen un valor mínimo menor o igual al máximo. El siguiente fragmento de código muestra cómo puedes implementar esta comprobación:

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 []

Si deseas agregar comprobaciones a un administrador de modelos, seguirías el mismo enfoque en tu subclase de Manager.

Si deseas agregar una comprobación a una clase de modelo, el enfoque es casi el mismo: la única diferencia es que la comprobación es un método de clase, no un método de instancia:

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

En versiones anteriores, los motores de plantillas no implementaban un método check().

Los textos traducidos son:

Los mensajes son comparables. Esto te permite escribir fácilmente pruebas:

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)

Escribiendo pruebas de integración

Dado el requisito de registrar ciertos controles cuando la aplicación carga, puede ser útil probar su integración dentro del marco de trabajo de controles del sistema. Esto se puede lograr utilizando la función call_command().

Por ejemplo, esta prueba demuestra que el parámetro de configuración SITE_ID debe ser un entero, un control incorporado desde el marco de trabajo de sitios <sites-system-checks>:

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")

Considera el siguiente control que emite una advertencia en la implementación si no se establece el parámetro de configuración personalizado ENABLE_ANALYTICS a 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

Dado que este control no levantará un error SystemCheckError, la presencia del mensaje de advertencia en la salida stderr se puede afirmar como sigue:

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())