# Documentation Django 5.2.8

# L’infrastructure des messages

Il est fréquent que les applications web ont besoin d’afficher des messages de notification (aussi appelés « messages flash ») pour les utilisateurs après le traitement d’un formulaire ou de tout autre type de saisie d’utilisateur.

Pour cela, Django offre une prise en charge complète des messages stockés dans des cookies ou dans les sessions, aussi bien pour des utilisateurs anonymes que des utilisateurs connectés. L’infrastructure des messages permet de stocker temporairement des messages dans une requête et de les récupérer ensuite dans une prochaine requête (en générale la suivante) pour les afficher. Chaque message est étiqueté avec un niveau spécifique déterminant sa priorité (par ex. `info`, `warning` ou `error`).

## Activation des messages

Les messages sont implémentés par une classe d’intergiciel accompagnée d’un processeur de contexte.

Le fichier `settings.py` par défaut créé par `django-admin startproject` contient déjà tous les réglages nécessaires pour activer la fonctionnalité des messages :

- `'django.contrib.messages'` figure dans `INSTALLED_APPS`.

- `MIDDLEWARE` contient `'django.contrib.sessions.middleware.SessionMiddleware'` et `'django.contrib.messages.middleware.MessageMiddleware'`.

  Le moteur de stockage par défaut dépend des sessions. C’est pourquoi `SessionMiddleware` doit être actif et apparaître avant `MessageMiddleware` dans `MIDDLEWARE`.

- L’option `'context_processors'` du moteur `DjangoTemplates` défini dans le réglage `TEMPLATES` contient `'django.contrib.messages.context_processors.messages'`.

Si vous ne souhaitez pas utiliser les messages, vous pouvez enlever `'django.contrib.messages'` du réglage `INSTALLED_APPS`, enlever la ligne `MessageMiddleware` du réglage `MIDDLEWARE` et enlever le processeur de contexte `messages` du réglage `TEMPLATES`.

## Configuration du moteur de message

### Moteurs de stockage

L’infrastructure des messages peut utiliser différents moteurs pour stocker temporairement les messages.

Django fournit trois classes de stockage intégrées dans `django.contrib.messages`:

class storage.session.SessionStorage

  Cette classe stocke tous les messages à l’intérieur de la session de la requête. Elle a donc besoin de l’application `contrib.sessions` de Django.

class storage.cookie.CookieStorage

  Cette classe stocke les données de messages dans un cookie (signé par l’empreinte secrète pour éviter d’être manipulé) pour que les notifications soient conservées d’une requête à l’autre. Les anciens messages sont effacés si la taille des données du cookie dépasse 2048 octets.

class storage.fallback.FallbackStorage

  Cette classe utilise d’abord `CookieStorage`, puis se rabat sur `SessionStorage` pour les messages qui ne rentrent pas dans un seul cookie. Elle exige également la présence de l’application `contrib.sessions` de Django.

  Ce comportement évite d’écrire dans la session autant que possible. Cela donne en général les meilleures performances.

`FallbackStorage` est la classe de stockage par défaut. Si elle ne correspond pas à vos besoins, vous pouvez choisir une autre classe de stockage en définissant `MESSAGE_STORAGE` au chemin complet d’importation de la classe, par exemple :

    MESSAGE_STORAGE = "django.contrib.messages.storage.cookie.CookieStorage"

class storage.base.BaseStorage

Pour écrire votre propre classe de stockage, héritez de la classe `BaseStorage` dans `django.contrib.messages.storage.base` et implémentez les méthodes `_get` et `_store`.

### Niveaux de messages

L’infrastructure des messages est basée sur une architecture de niveaux configurable et semblable à celle du module de journalisation de Python. Les niveaux de messages permettent de grouper des messages par type afin qu’ils puissent être filtrés ou affichés différemment dans les vues et les gabarits.

Les niveaux intégrés, qui peuvent être importés directement depuis `django.contrib.messages`, sont :

Constante | But
--- | ---
`DEBUG` | Messages liés au développement qui seront ignorés (ou supprimés) dans un déploiement en production.
`INFO` | Messages d’information pour l’utilisateur.
`SUCCESS` | Une action a réussi, par ex. « votre profil a été mis à jour avec succès ».
`WARNING` | Il n’y a pas eu d’erreur, mais il pourrait y en avoir une sous peu.
`ERROR` | Une action n’a pas réussi ou une autre erreur quelconque s’est produite.

Le réglage `MESSAGE_LEVEL` peut être utilisé pour modifier le niveau minimum enregistré (ou il peut être modifié selon la requête). Les tentatives d’ajouter des messages à un niveau inférieur à celui-ci seront ignorées.

### Étiquettes de messages

Les étiquettes de messages sont une représentation textuelle du niveau de message additionnée d’éventuelles étiquettes complémentaires ajoutées directement dans la vue (voir Ajout d’étiquettes de messages supplémentaires ci-dessous pour plus de détails). Les étiquettes sont stockées dans une chaîne et sont séparées par des espaces. Typiquement, les étiquettes de messages sont utilisées comme classes CSS pour personnaliser le style des messages en fonction de leur type. Par défaut, chaque niveau possède une seule étiquette sous forme d’une version en minuscules de sa constante :

Constante de niveau | Étiquette
--- | ---
`DEBUG` | `debug`
`INFO` | `info`
`SUCCESS` | `success`
`WARNING` | `warning`
`ERROR` | `error`

Pour modifier les étiquettes par défaut d’un niveau de message (intégré ou personnalisé), définissez le réglage `MESSAGE_TAGS` à un dictionnaire contenant les niveaux que vous souhaitez modifier. Comme cela étend les étiquettes par défaut, il suffit d’indiquer les étiquettes des niveaux que l’on souhaite remplacer :

    from django.contrib.messages import constants as messages

    MESSAGE_TAGS = {
        messages.INFO: "",
        50: "critical",
    }

## Utilisation des messages dans les vues et les gabarits

add_message(request, level, message, extra_tags='', fail_silently=False)

### Ajout d’un message

Pour ajouter un message, appelez :

    from django.contrib import messages

    messages.add_message(request, messages.INFO, "Hello world.")

Quelques méthodes de raccourci fournissent une manière standard d’ajouter des messages avec les étiquettes couramment utilisées (qui sont généralement représentées sous formes de classes HTML des messages) :

    messages.debug(request, "%s SQL statements were executed." % count)
    messages.info(request, "Three credits remain in your account.")
    messages.success(request, "Profile details updated.")
    messages.warning(request, "Your account expires in three days.")
    messages.error(request, "Document deleted.")

### Affichage des messages

get_messages(request)

Dans votre gabarit, utilisez quelque chose comme :

    {% if messages %}
    <ul class="messages">
        {% for message in messages %}
        <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
        {% endfor %}
    </ul>
    {% endif %}

Si vous utilisez le processeur de contexte, le gabarit devrait être produit avec un `RequestContext`. Sinon, assurez-vous que `messages` soit disponible dans le contexte du gabarit.

Même si vous savez qu’il n’y a qu’un message, il faut tout de même passer en boucle la liste des `messages`, sinon le message ne sera pas effacé de son stockage pour la requête suivante.

Le processeur de contexte fournit également une variable `DEFAULT_MESSAGE_LEVELS` qui fait correspondre les niveaux de messages à leur valeur numérique :

    {% if messages %}
    <ul class="messages">
        {% for message in messages %}
        <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
            {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}Important: {% endif %}
            {{ message }}
        </li>
        {% endfor %}
    </ul>
    {% endif %}

En dehors des gabarits, vous pouvez utiliser `get_messages()`:

    from django.contrib.messages import get_messages

    storage = get_messages(request)
    for message in storage:
        do_something_with_the_message(message)

Par exemple, vous pouvez récupérer tous les messages pour les renvoyer dans une réponse JSONResponseMixin au lieu d’une réponse `TemplateResponseMixin`.

`get_messages()` renvoie une instance du moteur de stockage configuré.

### La classe `Message`

class Message

  Lorsque vous parcourez en boucle la liste des messages dans un gabarit, vous obtenez des instances de la classe `Message`. Elles ne possèdent que quelques attributs :

  - `message`: le contenu textuel du message.

  - `level`: un nombre entier décrivant le type du message (voir la section sur les niveaux de messages ci-dessus).

  - `tags`: une chaîne combinant toutes les étiquettes de messages (`extra_tags` et `level_tag`) séparées par des espaces.

  - `extra_tags`: une chaîne contenant les étiquettes personnalisées du message, séparées par des espaces. Vide par défaut.

  - `level_tag`: la représentation textuelle du niveau. Par défaut, il s’agit de la version en minuscules du nom de la constante associée, mais cela peut être modifié en cas de besoin par l’emploi du réglage `MESSAGE_TAGS`.

### Création de niveaux de messages personnalisés

Les niveaux de messages ne sont en fait que des nombres entiers, vous pouvez donc facilement définir vos propres niveaux et les utiliser pour créer des retours d’information plus personnalisés, par exemple :

    CRITICAL = 50

    def my_view(request):
        messages.add_message(request, CRITICAL, "A serious error occurred.")

Si vous créez des niveaux de messages personnalisés, vous devriez éviter de remplacer des niveaux existants. Les valeurs des niveaux intégrés sont :

Constante de niveau | Valeur
--- | ---
`DEBUG` | 10
`INFO` | 20
`SUCCESS` | 25
`WARNING` | 30
`ERROR` | 40

Si vous avez besoin d’identifier les niveaux personnalisés dans du code HTML ou CSS, il est nécessaire de fournir une correspondance dans le réglage `MESSAGE_TAGS`.

Note

Si vous créez une application réutilisable, il est recommandé de n’utiliser que les niveaux de messages intégrés et de ne pas compter sur des niveaux personnalisés.

### Modification du niveau minimal enregistré par requête

Le niveau minimal enregistré peut être défini par requête au travers de la méthode `set_level`:

    from django.contrib import messages

    # Change the messages level to ensure the debug message is added.
    messages.set_level(request, messages.DEBUG)
    messages.debug(request, "Test message...")

    # In another request, record only messages with a level of WARNING and higher
    messages.set_level(request, messages.WARNING)
    messages.success(request, "Your profile was updated.")  # ignored
    messages.warning(request, "Your account is about to expire.")  # recorded

    # Set the messages level back to default.
    messages.set_level(request, None)

De même, le niveau actuel effectif peut être obtenu par `get_level`:

    from django.contrib import messages

    current_level = messages.get_level(request)

Pour plus d’informations sur le fonctionnement du niveau minimal enregistré, voir Niveaux de messages ci-dessus.

### Ajout d’étiquettes de messages supplémentaires

Pour un contrôle plus direct sur les étiquettes de messages, on peut indiquer une chaîne contenant des étiquettes supplémentaires aux diverses méthodes d’ajout :

    messages.add_message(request, messages.INFO, "Over 9000!", extra_tags="dragonball")
    messages.error(request, "Email box full", extra_tags="email")

Les étiquettes supplémentaires sont ajoutées avant l’étiquette par défaut du niveau et sont séparées par des espaces.

### Échec silencieux quand l’infrastructure des messages n’est pas installée

Lors de l’écriture d’une application réutilisable (ou autre bout de code) et que l’on veut inclure la fonctionnalité des messages mais sans exiger des utilisateurs son activation s’ils ne le souhaitent pas, il est possible de transmettre un paramètre nommé supplémentaire `fail_silently=True` aux diverses méthodes de la famille `add_message`. Par exemple :

    messages.add_message(
        request,
        messages.SUCCESS,
        "Profile details updated.",
        fail_silently=True,
    )
    messages.info(request, "Hello world.", fail_silently=True)

Note

En définissant `fail_silently=True`, cela ne fait que masquer les erreurs `MessageFailure` qui apparaissent normalement lorsque l’infrastructure des messages n’est pas active et que quelqu’un essaie d’utiliser l’une des méthodes de la famille `add_message`. Cela ne masque pas les erreurs qui pourraient se produire pour d’autres raisons.

### Ajout de messages dans les vues fondées sur les classes

class views.SuccessMessageMixin

  Ajoute un attribut de message de succès aux classes basées sur `FormView`.

  get_success_message(cleaned_data)

    `cleaned_data` correspond aux données nettoyées du formulaire utilisé pour la mise en forme de la chaîne.

Exemple de fichier views.py:

    from django.contrib.messages.views import SuccessMessageMixin
    from django.views.generic.edit import CreateView
    from myapp.models import Author

    class AuthorCreateView(SuccessMessageMixin, CreateView):
        model = Author
        success_url = "/success/"
        success_message = "%(name)s was created successfully"

Les données nettoyées de `form` sont disponibles pour l’interpolation de chaîne avec la syntaxe `%(nom_champ)s`. Pour les `ModelForm`, si vous avez besoin d’accéder aux champs de l’objet enregistré, surchargez la méthode `get_success_message()`.

Exemple de fichier views.py pour ModelForms :

    from django.contrib.messages.views import SuccessMessageMixin
    from django.views.generic.edit import CreateView
    from myapp.models import ComplicatedModel

    class ComplicatedCreateView(SuccessMessageMixin, CreateView):
        model = ComplicatedModel
        success_url = "/success/"
        success_message = "%(calculated_field)s was created successfully"

        def get_success_message(self, cleaned_data):
            return self.success_message % dict(
                cleaned_data,
                calculated_field=self.object.calculated_field,
            )

## Expiration des messages

Les messages sont marqués pour leur suppression lorsqu’on boucle sur leur instance de stockage (puis effacés lorsque la réponse est traitée).

Pour empêcher que les messages soient effacés, il est possible de définir le stockage des messages à `False` après l’itération :

    storage = messages.get_messages(request)
    for message in storage:
        do_something_with(message)
    storage.used = False

## Comportement des requêtes parallèles

En raison du fonctionnement des cookies (et donc des sessions), le comportement des moteurs qui se basent sur des cookies ou des sessions n’est pas défini lorsqu’un même client effectue plusieurs requêtes qui définissent ou lisent des messages en parallèle. Par exemple, si un client démarre une requête qui crée un message dans une fenêtre (ou un onglet) puis une autre qui récupère les messages non lus dans une autre fenêtre avant que la première fenêtre ait reçu sa redirection, il se peut que le message apparaisse dans la seconde fenêtre au lieu de la première où il aurait dû apparaître.

En bref, lorsque plusieurs requêtes simultanées proviennent du même client, il n’y a aucune garantie que les messages soient affichés dans la même fenêtre qui les a produits, ou qu’ils soient effectivement affichés quelque part. Notez que cela ne pose habituellement pas de problème dans la plupart des applications et que ce problème va disparaître dans HTML5 où chaque fenêtre/onglet possède son propre contexte de navigation.

## Réglages

Quelques réglages permettent de contrôler le comportement des messages :

- `MESSAGE_LEVEL`

- `MESSAGE_STORAGE`

- `MESSAGE_TAGS`

Pour les moteurs qui utilisent les cookies, les réglages du cookie sont tirés des réglages du cookie de session :

- `SESSION_COOKIE_DOMAIN`

- `SESSION_COOKIE_SECURE`

- `SESSION_COOKIE_HTTPONLY`

## Tests

Ce module offre une méthode d’assertion de test adaptée aux tests de messages liés aux réponses `HttpResponse`.

Pour profiter de cette assertion, ajoutez `MessagesTestMixin` à la hiérarchie de classe :

    from django.contrib.messages.test import MessagesTestMixin
    from django.test import TestCase

    class MsgTestCase(MessagesTestMixin, TestCase):
        pass

Puis, héritez de `MsgTestCase` dans vos tests.

MessagesTestMixin.assertMessages(response, expected_messages, ordered=True)

  Confirme que les `messages` ajoutés à la `réponse` correspondent à `expected_messages`.

  `expected_messages` est une liste d’objets `Message`.

  Par défaut, la comparaison est dépendante de l’ordre. Vous pouvez désactiver cela en définissant l’argument `ordered` à `False`.