# Documentation Django 5.2.8

# Infrastructure de contrôle du système

L’infrastructure de contrôle du système est un ensemble de contrôles statiques pour valider les projets Django. Elle détecte les problèmes courants et fournit des conseils sur la façon de les corriger. L’infrastructure est extensible de sorte que vous pouvez facilement ajouter vos propres contrôles.

Les contrôles peuvent être déclenchées de manière explicite par la commande `check`. Les contrôles sont déclenchés implicitement avant la plupart des commandes, y compris `runserver` et `migrate`. Pour des raisons de performance, les contrôles ne sont pas effectués dans le contexte de la pile WSGI utilisée en mode déployé. Si vous avez besoin d’effectuer les contrôles systèmes sur le serveur de déploiement, appelez explicitement la commande `check`.

Les erreurs graves empêcheront les commandes de Django (comme `runserver`) de fonctionner, tout court. Les problèmes mineurs sont signalés dans la console. Si vous avez inspecté la cause d’un avertissement et que vous êtes content de l’ignorer, vous pouvez masquer des avertissements spécifiques en utilisant le réglage `SILENCED_SYSTEM_CHECKS` dans votre fichier de réglages du projet.

Une liste complète de tous les contrôles pouvant être levées par Django peuvent être trouvés dans la référence des contrôles système.

## Écrire vos propres contrôles

Le système est souple et vous permet d’écrire des fonctions qui effectuent tout autre type de contrôle dont vous auriez besoin. Ce qui suit est un exemple de squelette d’une fonction de contrôle :

    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 fonction de contrôle doit accepter un argument `app_configs`; cet argument est la liste des applications qui doivent être inspectés. Si `None`, la vérification doit être effectué sur toutes les applications installées du projet.

Le contrôle recevra un paramètre nommé `databases`. Il s’agit d’une liste des alias de bases de données dont les connexions peuvent être utilisées pour inspecter la configuration au niveau base de données. Si `databases` vaut `None`, le contrôle ne doit utiliser aucune connexion de base de données.

Le paramètre `**kwargs` est nécessaire pour prendre en compte de futures modifications.

### Messages

La fonction doit renvoyer une liste de messages. Si aucun problème n’est détecté suite à la vérification, la fonction de contrôle doit renvoyer une liste vide.

Les avertissements et les erreurs levés par la méthode de contrôle doivent être des instances de `CheckMessage`. Une instance de `CheckMessage` encapsule une seule erreur à signaler ou un seul avertissement. Elle fournit également des conseils et le contexte applicable au message, et un identifiant unique qui est utilisé à des fins de filtrage.

Le concept est très similaire aux messages du système de message ou de la journalisation. Les messages sont étiquettés avec un `level` indiquant la gravité du message.

Il existe également des raccourcis pour faciliter la création des messages avec les niveaux de base. Lorsque vous utilisez ces classes, vous pouvez omettre le paramètre `level` car il est sous-entendu par le nom de la classe.

- `Debug`

- `Info`

- `Warning`

- `Error`

- `Critical`

### Enregistrement et étiquetage des contrôles

Finalement, votre fonction de contrôle doit être explicitement inscrite dans le registre des contrôles systèmes. Les contrôles doivent être inscrits dans un fichier qui est chargé au moment du chargement de l’application. Par exemple, dans la méthode `AppConfig.ready()`.

register(*tags)(function)

Vous pouvez passer autant d’étiquettes que vous le souhaitez à `register` pour labeliser votre contrôle. L’étiquetage des contrôles est utile car elle vous permet de n’exécuter qu’un certain groupe de contrôles. Par exemple, pour enregistrer un contrôle de compatibilité, vous feriez l’appel suivant :

    from django.core.checks import register, Tags

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

Vous pouvez inscrire des « contrôles de déploiement » qui ne sont applicables qu’à un fichier de réglages de production de la manière suivante :

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

Ces contrôles ne seront exécutés que si l’option `check --deploy` est utilisée.

Vous pouvez aussi employer `register` comme une fonction au lieu d’un décorateur en lui transmettant un objet exécutable (habituellement une fonction) comme premier paramètre.

Le code ci-dessous est équivalent au code ci-dessus :

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

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

### Field, constraint, model, manager, template engine, task, and database checks

Dans certains cas, vous n’aurez pas besoin d’enregistrer votre fonction de contrôle – vous pouvez la greffer sur un enregistrement existant.

Fields, constraints, models, model managers, template engines, task backends, and database backends all implement a `check()` method that is already registered with the check framework. If you want to add extra checks, you can extend the implementation on the base class, perform any extra checks you need, and append any messages to those generated by the base class. It’s recommended that you delegate each check to separate methods.

Prenons un exemple où vous implémentez un champ personnalisé nommé `RangedIntegerField`. Ce champ ajoute les arguments `min` et `max` au constructeur d”`IntegerField`. Vous pourriez ajouter une vérification afin de vous assurer que les utilisateurs fournissent une valeur min qui est inférieur ou égale à la valeur max. Le code suivant montre comment vous pouvez mettre en œuvre ce contrôle :

    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 vous vouliez ajouter des contrôles à un gestionnaire de modèle, vous prendriez la même approche avec votre sous-classe de `Manager`.

Si vous souhaitez ajouter un contrôle à une classe de modèle, l’approche est presque la même : la seule différence est que le contrôle est une méthode de classe, pas une méthode d’instance :

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

Changed in Django 6.0:

In older versions, constraints didn’t implement a `check()` method.

### Écriture de tests

Les messages sont comparables. Cela vous permet d’écrire facilement des tests :

    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)

#### Écriture de tests d’intégration

Étant donné le besoin d’inscrire certains contrôles au chargement de l’application, il peut être utile de tester leur intégration dans l’infrastructure des contrôles systèmes. Cela peut être fait en appelant la fonction `call_command()`.

Par exemple, ce test démontre que le réglage `SITE_ID` doit être un nombre entier, ce qui est fait par un contrôle intégré au système des sites:

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

Considérez le contrôle suivant qui émet un avertissement lors du déploiement si un réglage personnalisé nommé `ENABLE_ANALYTICS` n’est pas défini à `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

Sachant que ce contrôle ne produira pas de `SystemCheckError`, la présence du message d’avertissement dans la sortie `stderr` peut être testée comme ceci :

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