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=None,
                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. L’argument **kwargs est nécessaire pour 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.

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
New in Django 1.8.

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)
Changed in Django 1.8:

La possibilité d’utiliser register en tant que fonction a été ajoutée.

Contrôles sur les champs, modèles et gestionnaires

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

Les champs, les modèles et les gestionnaires de modèle implémentent tous une méthode check() qui est déjà enregistrée auprès du système de vérification. Si vous souhaitez ajouter des contrôles supplémentaires, vous pouvez étendre l’implémentation de la classe de base, effectuer tous contrôles supplémentaires nécessaires, et ajouter tous les messages à ceux générés par la classe de base. Il est recommandé que vous déléguiez chaque contrôle à des méthodes distinctes.

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(RangedIntegerField, self).__init__(**kwargs)
        self.min = min
        self.max = max

    def check(self, **kwargs):
        # Call the superclass
        errors = super(RangedIntegerField, self).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(MyModel, cls).check(**kwargs)
        # ... your own checks ...
        return errors

É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=None,
        obj=checked_object,
        id='myapp.E001',
    )
]
self.assertEqual(errors, expected_errors)
Back to Top