# Documentation Django 6.0.7

# Actions d’administration

Le mode de travail de base de l’administration de Django est en résumé : « sélectionner un objet, puis le changer ». Cela fonctionne bien pour la majorité des cas d’utilisation. Toutefois, si vous devez effectuer la même modification à plusieurs objets à la fois, ce mode de travail peut être assez fastidieux.

Dans ces cas, l’administration de Django vous permet d’écrire et d’inscrire des « actions » – des fonctions appelées avec une liste d’objets sélectionnés depuis la page de liste pour modification.

Si vous examinez n’importe quelle liste d’objets à modifier dans l’administration, vous verrez cette fonctionnalité en action ; Django est livré avec une action « supprimer les objets sélectionnés » disponible pour tous les modèles. Par exemple, voici le module utilisateur de l’application `django.contrib.auth` intégrée dans Django :

Avertissement

L’action « supprimer les objets sélectionnés » utilise `QuerySet.delete()` pour des raisons d’efficacité, entraînant une importante mise en garde : la méthode `delete()` de vos modèles ne sera pas appelée.

Si vous souhaitez modifier ce comportement, vous pouvez surcharger `ModelAdmin.delete_queryset()` ou écrire une action personnalisée qui s’occupe de la suppression à votre manière – par exemple, en appelant `Model.delete()` pour chacun des éléments sélectionnés.

Pour plus d’informations sur la suppression en vrac, voir la documentation sur la suppression d’objets.

Lisez la suite pour savoir comment ajouter vos propres actions à cette liste.

## Écriture d’actions

La meilleure façon d’expliquer les actions est de montrer un exemple. Allons-y !

Un cas d’utilisation courant pour les actions d’administration est la mise à jour en vrac d’un modèle. Imaginez une application de nouvelles avec un modèle `Article`:

    from django.db import models

    STATUS_CHOICES = {
        "d": "Draft",
        "p": "Published",
        "w": "Withdrawn",
    }

    class Article(models.Model):
        title = models.CharField(max_length=100)
        body = models.TextField()
        status = models.CharField(max_length=1, choices=STATUS_CHOICES)

        def __str__(self):
            return self.title

Une tâche courante que nous pourrions réaliser avec un tel modèle est de mettre à jour l’état d’un article de « brouillon » à « publié ». Nous pourrions facilement le faire dans l’administration un article à la fois, mais si nous voulions publier en vrac une série d’articles, ce serait fastidieux. Écrivons donc une action qui nous permet de modifier l’état d’un article à « publié ».

### Écriture des fonctions d’action

Tout d’abord, nous devons écrire une fonction qui est appelée quand l’action est déclenchée depuis l’administration. Les fonctions d’action sont des fonctions ordinaires qui acceptent trois paramètres :

- La classe `ModelAdmin` concernée

- Un objet `HttpRequest` représentant la requête actuelle

- Un jeu de requête `QuerySet` contenant l’ensemble des objets sélectionnés par l’utilisateur.

Notre fonction de publication d’articles n’aura pas besoin de la classe `ModelAdmin` ni de l’objet requête, mais nous allons utiliser le jeu de requête :

    def make_published(modeladmin, request, queryset):
        queryset.update(status="p")

Note

Pour de meilleures performances, nous utiliserons la méthode update (mise à jour) du jeu de requête. D’autres types d’actions pourraient avoir besoin de manipuler chaque objet individuellement ; dans ce cas, nous itérerions sur le jeu de requête :

    for obj in queryset:
        do_something_with(obj)

C’est en fait tout ce qu’il y a besoin d’écrire pour une action ! Toutefois, nous allons effectuer une étape supplémentaire facultative mais utile et donner un « joli » titre à l’action dans l’interface d’administration. Par défaut, cette action apparaît dans la liste d’actions comme « Make published » – le nom de la fonction avec les soulignements remplacés par des espaces. C’est bien, mais nous pouvons fournir un nom meilleur et plus convivial en utilisant le décorateur `action()` sur la fonction `make_published`:

    from django.contrib import admin

    ...

    @admin.action(description="Mark selected stories as published")
    def make_published(modeladmin, request, queryset):
        queryset.update(status="p")

Note

Cela peut sembler familier ; l’option d’administration `list_display` utilise aussi une technique similaire avec le décorateur `display()` pour fournir des descriptions conviviales pour les fonctions de rappel qui y sont inscrites.

### Ajout d’actions à la classe `ModelAdmin`

Ensuite, il faudra informer la `ModelAdmin` de l’action. Cela fonctionne comme n’importe quelle autre option de configuration. Ainsi, le fichier `admin.py` complet avec l’action et son inscription ressemblerait à :

    from django.contrib import admin
    from myapp.models import Article

    @admin.action(description="Mark selected stories as published")
    def make_published(modeladmin, request, queryset):
        queryset.update(status="p")

    class ArticleAdmin(admin.ModelAdmin):
        list_display = ["title", "status"]
        ordering = ["title"]
        actions = [make_published]

    admin.site.register(Article, ArticleAdmin)

Avec ce code, la liste d’objets à modifier dans l’interface d’administration ressemble à ceci :

C’est vraiment tout ce qu’il y a à faire ! Si vous brûlez d’écrire vos propres actions, vous en savez maintenant assez pour débuter. Le reste de ce document couvre des techniques plus avancées.

### Gestion des erreurs dans les actions

S’il y a des conditions d’erreur prévisibles qui peuvent survenir lors de l’exécution de votre action, vous devriez faire amende honorable en informant l’utilisateur du problème. Cela signifie gérer les exceptions et utiliser `django.contrib.admin.ModelAdmin.message_user()` pour afficher une description conviviale du problème dans la réponse.

## Techniques d’action avancées

Il y a quelques options et possibilités supplémentaires que vous pouvez exploiter pour des options plus avancées.

### Actions en tant que méthodes de `ModelAdmin`

L’exemple ci-dessus montre l’action `make_published` définie comme une fonction. C’est tout à fait correct, mais ce n’est pas parfait d’un point de vue conception de code : comme l’action est étroitement couplée à l’objet `Article`, il paraît logique de fusionner l’action dans l’objet `ArticleAdmin` même.

Vous pouvez le faire comme ceci

    class ArticleAdmin(admin.ModelAdmin):
        ...

        actions = ["make_published"]

        @admin.action(description="Mark selected stories as published")
        def make_published(self, request, queryset):
            queryset.update(status="p")

Remarquez d’abord que nous avons transformé `make_published` en une méthode et renommé le paramètre `modeladmin` à `self`, puis que nous avons placé la chaîne `make_published` dans `actions` à la place d’une référence de fonction directe. Cela indique à `ModelAdmin` de rechercher l’action en tant que méthode.

Définir les actions en tant que méthodes donne à l’action un accès plus idiomatique à la classe `ModelAdmin` elle-même, permettant à l’action d’appeler l’une des méthodes définies par l’application d’administration.

Par exemple, nous pouvons utiliser `self` pour afficher un message à l’utilisateur l’informant que l’action a réussi :

    from django.contrib import messages
    from django.utils.translation import ngettext

    class ArticleAdmin(admin.ModelAdmin):
        ...

        def make_published(self, request, queryset):
            updated = queryset.update(status="p")
            self.message_user(
                request,
                ngettext(
                    "%d story was successfully marked as published.",
                    "%d stories were successfully marked as published.",
                    updated,
                )
                % updated,
                messages.SUCCESS,
            )

This makes the action match what the admin itself does after successfully performing an action:

### Actions qui fournissent des pages intermédiaires

Par défaut, après qu’une action a été effectuée, l’utilisateur est redirigé vers la page d’origine de la liste d’édition. Cependant, certaines actions, en particulier les plus complexes, pourraient devoir renvoyer à des pages intermédiaires. Par exemple, l’action de suppression intégrée demande confirmation avant de supprimer les objets sélectionnés.

Pour fournir une page intermédiaire, renvoyez une réponse `HttpResponse` (ou une sous-classe) depuis votre action. Par exemple, vous pourriez écrire une fonction d’exportation qui utilise les fonctions de sérialisation de Django pour exporter certains objets sélectionnés en JSON :

    from django.core import serializers
    from django.http import HttpResponse

    def export_as_json(modeladmin, request, queryset):
        response = HttpResponse(content_type="application/json")
        serializers.serialize("json", queryset, stream=response)
        return response

En général, l’exemple ci-dessus n’est pas considéré comme une bonne idée. La plupart du temps, la pratique conseillée est de renvoyer une réponse `HttpResponseRedirect` et de rediriger l’utilisateur vers une vue que vous avez écrite, en passant la liste des objets sélectionnés dans la chaîne de requête GET. Cela vous permet de fournir une logique d’interaction complexe sur les pages intermédiaires. Par exemple, si vous vouliez fournir une fonction d’exportation plus complète, vous voudriez que l’utilisateur puisse choisir un format et peut-être une liste de champs à inclure dans l’exportation. La meilleure chose à faire serait d’écrire une petite action qui redirige vers la vue d’exportation personnalisée :

    from django.contrib.contenttypes.models import ContentType
    from django.http import HttpResponseRedirect

    def export_selected_objects(modeladmin, request, queryset):
        selected = queryset.values_list("pk", flat=True)
        ct = ContentType.objects.get_for_model(queryset.model)
        return HttpResponseRedirect(
            "/export/?ct=%s&ids=%s"
            % (
                ct.pk,
                ",".join(str(pk) for pk in selected),
            )
        )

Comme vous pouvez le voir, l’action est plutôt succinte ; toute la logique complexe est déléguée à la vue d’exportation. Celle-ci doit se charger de traiter des objets de tout type, d’où la présence du modèle `ContentType`.

La rédaction de cette vue est laissée en exercice au lecteur.

### Actions disponibles pour l’ensemble d’un site

AdminSite.add_action(action, name=None)

  Certaines actions sont plus efficaces lorsqu’elles sont mises à la disposition de tous les objets dans le site d’administration – l’action d’exportation définie ci-dessus serait un bon candidat. Vous pouvez rendre une action globalement disponible en utilisant `AdminSite.add_action()`. Par exemple :

      from django.contrib import admin

      admin.site.add_action(export_selected_objects)

  Cela rend l’action `export_selected_objects` globalement disponible en tant qu’action nommée « export_selected_objects ». Vous pouvez explicitement donner un nom à l’action – utile si vous souhaitez plus tard programmatiquement supprimer l’action – en passant un second argument à `AdminSite.add_action()`

      admin.site.add_action(export_selected_objects, "export_selected")

### Désactivation des actions

Parfois, vous devez désactiver certaines actions – en particulier celles enregistrées globalement – pour des objets particuliers. Il y a plusieurs façons dont vous pouvez désactiver les actions :

#### Désactivation d’une action à l’échelle du site

AdminSite.disable_action(name)

  Si vous avez besoin de désactiver une action à l’échelle du site vous pouvez appeler `AdminSite.disable_action()`.

  Par exemple, vous pouvez utiliser cette méthode pour supprimer l’action intégrée « supprimer les objets sélectionnés » :

      admin.site.disable_action("delete_selected")

  Une fois que vous avez fait ce qui précède, cette action ne sera plus disponible à l’ensemble du site.

  Si toutefois vous avez besoin de réactiver une action globalement désactivée pour un modèle en particulier, il suffit de l’indiquer explicitement dans la liste `ModelAdmin.actions`:

      # Globally disable delete selected
      admin.site.disable_action("delete_selected")

      # This ModelAdmin will not have delete_selected available
      class SomeModelAdmin(admin.ModelAdmin):
          actions = ["some_other_action"]
          ...

      # This one will
      class AnotherModelAdmin(admin.ModelAdmin):
          actions = ["delete_selected", "a_third_action"]
          ...

#### Désactivation de toutes les actions pour une classe `ModelAdmin` particulière

Si vous ne voulez aucune action en vrac disponible pour une classe `ModelAdmin` donnée, définissez `ModelAdmin.actions` à `None`:

    class MyModelAdmin(admin.ModelAdmin):
        actions = None

Cela indique à la classe `ModelAdmin` de n’afficher ni d’autoriser aucune des actions, y compris les actions globales au site.

#### Activation ou désactivation conditionnelle des actions

ModelAdmin.get_actions(request, action_location=ActionLocation.CHANGE_LIST)

  Enfin, vous pouvez activer ou désactiver conditionnellement les actions selon la requête (et donc par utilisateur) en redéfinissant `ModelAdmin.get_actions()`.

  This returns a dictionary of actions allowed for the specific `action_location`. The keys are action names, and the values are `Action` objects.

  Par exemple, si vous souhaitez que seuls les utilisateurs dont le nom commence par « J » puissent supprimer des objets en masse

      class MyModelAdmin(admin.ModelAdmin):
          ...

          def get_actions(self, request, action_location=ActionLocation.CHANGE_LIST):
              actions = super().get_actions(request, action_location=action_location)
              if request.user.username[0].upper() != "J":
                  if "delete_selected" in actions:
                      del actions["delete_selected"]
              return actions

  Changed in Django 6.1:

  The keyword argument `action_location` was added. The return type was changed to a dictionary where the keys are action names and the values are `Action` objects, previously the values were `(function, name, description)` tuples.

### Définition de permissions pour les actions

Les actions peuvent restreindre leur accès aux utilisateurs ayant des permissions particulières en enveloppant leur fonction d’action dans le décorateur `action()` et en lui passant l’argument `permissions`:

    @admin.action(permissions=["change"])
    def make_published(modeladmin, request, queryset):
        queryset.update(status="p")

L’action `make_published()` ne sera disponible que pour les utilisateurs qui passent le contrôle `ModelAdmin.has_change_permission()`.

Si `permissions` possède plus d’une permission, l’action sera disponible si l’utilisateur passe au moins l’un des contrôles.

Les valeurs disponibles pour `permissions` et leur méthodes de contrôle correspondantes sont :

- `'add'` (ajout) : `ModelAdmin.has_add_permission()`

- `'change'` (modification) : `ModelAdmin.has_change_permission()`

- `'delete'` (suppression) : `ModelAdmin.has_delete_permission()`

- `'view'` (affichage) : `ModelAdmin.has_view_permission()`

Vous pouvez indiquer d’autres valeurs pour autant que vous implémentiez une méthode correspondante `has_<valeur>_permission(self, request)` pour la classe `ModelAdmin` concernée.

Par exemple :

    from django.contrib import admin
    from django.contrib.auth import get_permission_codename

    class ArticleAdmin(admin.ModelAdmin):
        actions = ["make_published"]

        @admin.action(permissions=["publish"])
        def make_published(self, request, queryset):
            queryset.update(status="p")

        def has_publish_permission(self, request):
            """Does the user have the publish permission?"""
            opts = self.opts
            codename = get_permission_codename("publish", opts)
            return request.user.has_perm("%s.%s" % (opts.app_label, codename))

#### Controlling where actions are available

New in Django 6.1.

By default, admin actions are available on the change list page only. You can control where an action appears using the `location` argument of the `@admin.action` decorator.

For example, to make an action available only on the change form, set `location` to `ActionLocation.CHANGE_FORM`:

    from django.contrib import admin
    from django.contrib.admin import ActionLocation

    @admin.action(location=ActionLocation.CHANGE_FORM)
    def make_published(modeladmin, request, queryset): ...

To make an action available on both the change list and the change form:

    @admin.action(
        location=[ActionLocation.CHANGE_FORM, ActionLocation.CHANGE_LIST],
        description="Publish",
        description_plural="Mark selected stories as published",
    )
    def make_published(modeladmin, request, queryset): ...

Notice that `description` and `description_plural` were provided. These are optional but the admin action will be labeled by `description` in the admin change form and `description_plural` in the admin change list.

You can customize how actions are rendered by overriding admin templates. The change list page uses `admin/actions.html` and the change form page uses `admin/change_form_actions.html`. Note that the change form template inherits from the change list actions template.

## Le décorateur `action`

action(*, permissions=None, description=None, description_plural=None, location=ActionLocation.CHANGE_LIST)

  Ce décorateur permet de définir des attributs spécifiques sur les fonctions d’action personnalisées pouvant être utilisée dans `actions`:

      @admin.action(
          permissions=["publish"],
          description="Mark selected stories as published",
      )
      def make_published(self, request, queryset):
          queryset.update(status="p")

  Ceci équivaut à définir certains attributs directement sur la fonction (avec les noms originaux, plus longs)

      def make_published(self, request, queryset):
          queryset.update(status="p")

      make_published.allowed_permissions = ["publish"]
      make_published.short_description = "Mark selected stories as published"

  L’utilisation de ce décorateur n’est pas obligatoire pour créer une fonction d’action, mais il peut être utile de l’utiliser sans argument dans votre code source comme marqueur de mise en évidence du but de la fonction

      @admin.action
      def make_inactive(self, request, queryset):
          queryset.update(is_active=False)

  Dans cas, aucun attribut ne sera ajouté à la fonction.

  Paramètres:

    - permissions – A list of permission codenames that restrict the action to users who have at least one of the defined permissions. This sets the `allowed_permissions` attribute on the function. See Définition de permissions pour les actions for details.

    - description – A human-readable description of the action to be rendered in the admin. If `description` is not provided, Django renders the function’s name, converting underscores to spaces and capitalizing the first letter of the first word. This sets the `short_description` attribute on the function.

    - description_plural – A human-readable description of the action used in contexts where plural wording is required, such as on the admin change list. If `description_plural` is not provided, falls back to `description`. This sets the `plural_description` attribute on the function.

    - location – Specifies where the action is available. Accepts either a single `ActionLocation` value or an iterable of values. If omitted, the action is only available on the admin change list. See Controlling where actions are available for details.

  Action description `%`-formatting support

  Action descriptions support `%`-formatting and may include `'%(verbose_name)s'` and `'%(verbose_name_plural)s'` placeholders. These are replaced with the model’s `verbose_name` and `verbose_name_plural`.

  Changed in Django 6.1:

  The keyword arguments `description_plural` and `location` were added.

## `ActionLocation`

New in Django 6.1.

class ActionLocation

  Enum of allowed values for the `location` parameter of the `action()` decorator.

  CHANGE_FORM

    The action is available on the admin change form. When an action is run, any unsaved changes on the admin change form will be lost.

  CHANGE_LIST

    The action is available on the admin change list.

## `Action`

New in Django 6.1.

class Action

  Represents an action. Actions should be defined using the `action()` decorator.

  func

    The action function. See Écriture des fonctions d’action for details.

  name

    The action function name.

  description

    A human-readable description of the action to be rendered in the admin.

  plural_description

    A human-readable description of the action used in contexts where plural wording is required.

  locations

    A list of `ActionLocation` values the admin action can be rendered.