Administrativa åtgärderLink to this heading

Det grundläggande arbetsflödet i Djangos admin är, i ett nötskal, ”välj ett objekt och ändra det sedan” Detta fungerar bra för en majoritet av användningsfallen. Men om du behöver göra samma ändring på många objekt samtidigt kan detta arbetsflöde vara ganska tråkigt.

I dessa fall låter Djangos admin dig skriva och registrera ”åtgärder” - funktioner som anropas med en lista över objekt som valts på sidan med ändringslistan.

Om du tittar på någon ändringslista i administratören ser du den här funktionen i aktion; Django levereras med en åtgärd ”ta bort valda objekt” som är tillgänglig för alla modeller. Till exempel:, här är användarmodulen från Djangos inbyggda django.contrib.auth app:

/sv/6.1/_images/admin-actions.png

Läs vidare för att ta reda på hur du lägger till dina egna åtgärder i listan.

Skriva åtgärderLink to this heading

Det enklaste sättet att förklara åtgärder är genom exempel, så låt oss dyka ner i det.

Ett vanligt användningsfall för adminåtgärder är massuppdatering av en modell. Föreställ dig en nyhetsapplikation med en Artikel-modell:

Code
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

En vanlig uppgift som vi kan utföra med en sådan här modell är att uppdatera en artikels status från ”utkast” till ”publicerad”. Vi kan enkelt göra detta i administratören för en artikel i taget, men om vi vill masspublicera en grupp artiklar skulle det vara tråkigt. Så låt oss skriva en åtgärd som låter oss ändra en artikels status till ”publicerad”

Skriva handlingsfunktionerLink to this heading

Först måste vi skriva en funktion som anropas när åtgärden utlöses från administratören. Åtgärdsfunktioner är vanliga funktioner som tar tre argument:

  • Den aktuella ModelAdmin

  • En HttpRequest som representerar den aktuella begäran,

  • En QuerySet som innehåller den uppsättning objekt som användaren har valt.

Vår funktion publish-these-articles kommer inte att behöva ModelAdmin eller request-objektet, men vi kommer att använda queryset:

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

Det är faktiskt allt som krävs för att skriva en åtgärd! Vi ska dock ta ytterligare ett valfritt men användbart steg och ge åtgärden en ”fin” titel i admin. Som standard visas den här åtgärden i åtgärdslistan som ”Gör publicerad” - funktionsnamnet, med understreck ersatta av mellanslag. Det är bra, men vi kan ge ett bättre, mer människovänligt namn genom att använda action()-dekoratorn på make_published-funktionen:

Code
from django.contrib import admin

...


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

Lägga till åtgärder till ModelAdminLink to this heading

Därefter måste vi informera vår ModelAdmin om åtgärden. Detta fungerar precis som alla andra konfigurationsalternativ. Så, den kompletta admin.py med åtgärden och dess registrering skulle se ut så här:

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

Den koden kommer att ge oss en ändringslista för administratörer som ser ut ungefär så här:

/sv/6.1/_images/adding-actions-to-the-modeladmin.png

Det är verkligen allt som behövs! Om du är sugen på att skriva dina egna åtgärder vet du nu tillräckligt för att komma igång. Resten av det här dokumentet handlar om mer avancerade tekniker.

Hantering av fel i åtgärderLink to this heading

Om det finns förutsebara felförhållanden som kan uppstå när du kör din åtgärd, bör du på ett elegant sätt informera användaren om problemet. Detta innebär att hantera undantag och använda django.contrib.admin.ModelAdmin.message_user() för att visa en användarvänlig beskrivning av problemet i svaret.

Avancerade handlingsteknikerLink to this heading

Det finns ett par extra alternativ och möjligheter som du kan utnyttja för mer avancerade alternativ.

Åtgärder som ModelAdmin-metoderLink to this heading

I exemplet ovan är åtgärden make_published definierad som en funktion. Det är helt okej, men det är inte perfekt ur en koddesignsynpunkt: eftersom åtgärden är tätt kopplad till objektet Article är det vettigt att koppla åtgärden till själva objektet ArticleAdmin.

Du kan göra det så här:

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

Lägg först märke till att vi har flyttat make_published till en metod och bytt namn på parametern modeladmin till self, och sedan att vi nu har lagt strängen 'make_published i actions istället för en direkt funktionsreferens. Detta säger till ModelAdmin att leta upp åtgärden som en metod.

Genom att definiera åtgärder som metoder får åtgärden en mer idiomatisk tillgång till själva ModelAdmin, vilket gör att åtgärden kan anropa vilken som helst av de metoder som tillhandahålls av administratören.

Vi kan t.ex. använda self för att skicka ett meddelande till användaren om att åtgärden var lyckad:

Code
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,
        )

Detta gör att åtgärden motsvarar vad admin själv gör efter att en åtgärd har utförts framgångsrikt:

/sv/6.1/_images/actions-as-modeladmin-methods.png

Åtgärder som ger mellanliggande sidorLink to this heading

Som standard omdirigeras användaren tillbaka till den ursprungliga sidan i ändringslistan efter att en åtgärd har utförts. Vissa åtgärder, särskilt mer komplexa sådana, måste dock returnera mellanliggande sidor. Till exempel: ber den inbyggda åtgärden delete om bekräftelse innan de valda objekten raderas.

För att tillhandahålla en mellanliggande sida, returnera en HttpResponse (eller underklass) från din åtgärd. Du kan till exempel skriva en exportfunktion som använder Djangos serialiseringsfunktioner för att dumpa några utvalda objekt som JSON:

Code
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

Generellt sett anses något som ovanstående inte vara en bra idé. För det mesta är den bästa metoden att returnera en HttpResponseRedirect och omdirigera användaren till en vy som du har skrivit och skicka listan över valda objekt i GET-frågesträngen. Detta gör att du kan tillhandahålla komplex interaktionslogik på de mellanliggande sidorna. Om du till exempel vill tillhandahålla en mer komplett exportfunktion skulle du vilja låta användaren välja ett format och eventuellt en lista över fält som ska ingå i exporten. Det bästa du kan göra är att skriva en liten åtgärd som omdirigerar till din anpassade exportvy:

Code
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),
        )
    )

Som du kan se är åtgärden ganska kort; all komplex logik hör hemma i din exportvy. Denna skulle behöva hantera objekt av alla typer, därav verksamheten med ContentType.

Att skriva denna vy lämnas som en övning till läsaren.

Göra åtgärder tillgängliga för hela webbplatsenLink to this heading

AdminSite.add_action(action, name=None)Link to this definition

Vissa åtgärder är bäst när de är tillgängliga för alla objekt på adminwebbplatsen – exportåtgärden som definieras ovan är en bra kandidat. Du kan göra en åtgärd globalt tillgänglig med AdminSite.add_action(). Till exempel:

Code
from django.contrib import admin

admin.site.add_action(export_selected_objects)

Detta gör åtgärden export_selected_objects globalt tillgänglig med namnet ”export_selected_objects”. Du kan uttryckligen ge åtgärden ett namn – bra om du senare vill ta bort åtgärden programmässigt – genom att skicka ett andra argument till AdminSite.add_action():

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

Inaktivera åtgärderLink to this heading

Ibland behöver du inaktivera vissa åtgärder - särskilt de som registered site-wide - för vissa objekt. Det finns några olika sätt att inaktivera åtgärder:

Inaktivera en webbplatsomfattande åtgärdLink to this heading

AdminSite.disable_action(name)Link to this definition

Om du behöver inaktivera en åtgärd för hela webbplatsen kan du anropa AdminSite.disable_action().

Du kan t.ex. använda den här metoden för att ta bort den inbyggda åtgärden ”ta bort markerade objekt”:

Code
admin.site.disable_action("delete_selected")

När du har gjort ovanstående kommer den åtgärden inte längre att vara tillgänglig på hela webbplatsen.

Om du däremot behöver återaktivera en globalt inaktiverad åtgärd för en viss modell, listar du den uttryckligen i listan ModelAdmin.actions:

Code
# 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"]
    ...

Inaktivera alla åtgärder för en viss ModelAdminLink to this heading

Om du vill ha inga bulkåtgärder tillgängliga för en viss ModelAdmin, sätt ModelAdmin.actions till None:

Code
class MyModelAdmin(admin.ModelAdmin):
    actions = None

Detta säger till ModelAdmin att inte visa eller tillåta några åtgärder, inklusive alla site-wide actions.

Villkorlig aktivering eller inaktivering av åtgärderLink to this heading

ModelAdmin.get_actions(request, action_location=ActionLocation.CHANGE_LIST)Link to this definition

Slutligen kan du villkorligt aktivera eller inaktivera åtgärder per begäran (och därmed per användare) genom att åsidosätta ModelAdmin.get_actions().

Detta returnerar en ordbok med åtgärder som är tillåtna för den angivna action_location. Nycklarna är åtgärdsnamn och värdena är Action-objekt.

Om du t.ex. bara vill att användare vars namn börjar med ”J” ska kunna radera objekt i bulk:

Code
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

Ange behörigheter för åtgärderLink to this heading

Åtgärder kan begränsa sin tillgänglighet till användare med specifika behörigheter genom att omsluta åtgärdsfunktionen med action()-dekoratorn och skicka argumentet permissions:

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

Åtgärden make_published() är endast tillgänglig för användare som passerar kontrollen ModelAdmin.has_change_permission() när den anropas med obj=None.

Om permissions har mer än en behörighet kommer åtgärden att vara tillgänglig så länge som användaren klarar minst en av kontrollerna.

Tillgängliga värden för permissions och motsvarande metodkontroller är:

Du kan ange vilket annat värde som helst så länge du implementerar en motsvarande has_<value>_permission(self, request)-metod på ModelAdmin.

Till exempel:

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

Administrationen kontrollerar inte automatiskt behörigheter på objektnivå för varje valt objekt när en åtgärd körs. Utför kontrollerna i själva åtgärden för att begränsa vilka objekt som en åtgärd får ändra utifrån behörigheter på objektnivå. Till exempel:

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

    @admin.action(permissions=["change"])
    def make_published(self, request, queryset):
        for obj in queryset:
            if self.has_change_permission(request, obj=obj):
                obj.status = "p"
                obj.save(update_fields=["status"])

Styra var åtgärder är tillgängligaLink to this heading

Som standard är adminåtgärder endast tillgängliga på ändringslistsidan. Du kan styra var en åtgärd visas med argumentet location till dekoratorn @admin.action.

För att till exempel göra en åtgärd tillgänglig endast i ändringsformuläret anger du location till ActionLocation.CHANGE_FORM:

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


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

För att göra en åtgärd tillgänglig både i ändringslistan och i ändringsformuläret:

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

Observera att description och description_plural angavs. De är valfria, men adminåtgärden etiketteras med description i adminens ändringsformulär och description_plural i adminens ändringslista.

Du kan anpassa hur åtgärder återges genom att åsidosätta adminmallar. Ändringslistsidan använder admin/actions.html och ändringsformuläret använder admin/change_form_actions.html. Observera att ändringsformulärmallen ärver från mallen för ändringslistans åtgärder.

Dekoratorn ”actionLink to this heading

action(*, permissions=None, description=None, description_plural=None, location=ActionLocation.CHANGE_LIST)Link to this definition

Denna dekorator kan användas för att ställa in specifika attribut på anpassade åtgärdsfunktioner som kan användas med actions:

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

Detta motsvarar att ange vissa attribut (med de ursprungliga, längre namnen) på funktionen direkt:

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


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

Det är inte obligatoriskt att använda denna dekorator för att skapa en actionfunktion, men det kan vara användbart att använda den utan argument som en markör i källan för att identifiera syftet med funktionen:

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

I detta fall kommer det inte att lägga till några attribut till funktionen.

Parametrar:
  • permissions – En lista med kodnamn för behörigheter som begränsar åtgärden till användare som har minst en av de angivna behörigheterna. Detta sätter funktionens attribut allowed_permissions. Se Ange behörigheter för åtgärder för mer information.

  • description – En läsbar beskrivning av åtgärden som ska återges i admin. Om description inte anges återger Django funktionsnamnet genom att ersätta understreck med mellanslag och göra första bokstaven i det första ordet till versal. Detta sätter funktionens attribut short_description.

  • description_plural – En läsbar beskrivning av åtgärden som används i sammanhang där plural formulering krävs, såsom i adminens ändringslista. Om description_plural inte anges används description i stället. Detta sätter funktionens attribut plural_description.

  • location – Anger var åtgärden är tillgänglig. Accepterar antingen ett enskilt ActionLocation-värde eller en itererbar samling värden. Om värdet utelämnas är åtgärden endast tillgänglig i adminens ändringslista. Se Styra var åtgärder är tillgängliga för mer information.

ActionLocationLink to this heading

class ActionLocationLink to this definition

Uppräkning av tillåtna värden för parametern location till dekoratorn action().

CHANGE_FORMLink to this definition

Åtgärden är tillgänglig i adminens ändringsformulär. När en åtgärd körs går eventuella osparade ändringar i adminens ändringsformulär förlorade.

CHANGE_LISTLink to this definition

Åtgärden är tillgänglig i adminens ändringslista.

ActionLink to this heading

class ActionLink to this definition

Representerar en åtgärd. Åtgärder ska definieras med dekoratorn action().

funcLink to this definition

Åtgärdsfunktionen. Se Skriva handlingsfunktioner för mer information.

nameLink to this definition

Åtgärdsfunktionens namn.

descriptionLink to this definition

En läsbar beskrivning av åtgärden som ska återges i admin.

plural_descriptionLink to this definition

En läsbar beskrivning av åtgärden som används i sammanhang där plural formulering krävs.

locationsLink to this definition

En lista över ActionLocation-värden där adminåtgärden kan återges.