Die Administrationsoberfläche ausbauen

Wir wollen die Event-Verwaltung noch ein bisschen ausbauen. Die erste Änderung soll eine kleine Schönheitskorrektur auf der Event-Detailseite sein. Aktuell kann man die Mingroups in einem Drop-Down-Feld auswählen. Wir wollen dies ändern und aus dem Drop-Down-Feld ein Reihe von Radio-Buttons machen, die vertikal angeordnet sind.

Choice-Felder als Radio Buttons in der Admin darstellen

Wir öffnen die Datei event_manager/events/admin.py und modifizieren das EventAdmin-Klasse:

@admin.register(Event)
class EventAdmin(admin.ModelAdmin):

    # anderer Code

    radio_fields = {
        "min_group": admin.VERTICAL,
    }

Die Eigenschaft radio_fields der ModelAdmin-Klasse ermöglicht uns komfortabel das Ändern der Eigenschaft:

../_images/admin_5_min_group.png

Admin Actions

Nun möchten wir auf der Events-Übersicht mehrere Events markieren und alle auf einen Schlag inaktiv setzen können. Bisher gibt es im Aktion-Dropdown nur eine Aktion: ausgewählte Events löschen.

../_images/admin_6_actions.png

Wollten wir aber ausgewählte Events nicht löschen, sondern nur auf aktiv bzw. inaktiv stellen, müssen wir jedes Event anklicken und auf der Eventseite die is_active-Eigenschaft ändern. Das ist mühselig, wenn man das bei mehreren Events machen möchte. Dafür gibt es sogenannte Actions.

Wir fügen nun diese beiden Methoden hier in usere EventAdmin-Klasse ein:

@admin.register(Event)
class EventAdmin(admin.ModelAdmin):

    # ... mehr Code
    list_filter = ("category",)
    list_display = "date", "author", "name", "category"
    actions = ["make_active", "make_inactive"]

    @admin.action(description="Setze Events active")
    def make_active(self, request, queryset):
        """Set all Events to active."""
        queryset.update(is_active=True)

    @admin.action(description="Setze Events inactive")
    def make_inactive(self, request, queryset):
        """Set all Events to inactive."""
        queryset.update(is_active=False)

Der @admin.action-Decorator ist optional und macht nichts weiter, als der Aktion in dem Aktionen-Dropdown-Feld einen greifbaren Namen zu geben. Interessant sind jetzt die beiden Methoden make_active bzw. make_inactive.

Diese beiden Methoden unserer EventAdmin-Klasse haben den Parameter queryset. Dieses queryset entspricht den auswählten also markierteren Events in der Eventübersicht. Führt man nun eine Aktion aus, bekommt die jeweilige Methode diese Events als queryset übergeben und wir können jetzt eine Aktion auf jeden dieser Events ausführen.

make_active zum Beispiel setzt die is_active Eigenschaft auf true und aktiviert damit die ausgewählten Events. Der @admin.action-Dekorator setzt noch die Eigenschaft description auf einen sprechenden Namen.

@admin.action(description="Setze Events active")
def make_active(self, request, queryset):
    """Set all Events to active."""
    queryset.update(is_active=True)

Damit diese beiden Methoden jetzt in der Admin auch registriert sind, müssen wir die Methoden-Referenzen an die actions-Liste übergeben.

actions = ["make_active", "make_inactive"]

Mehr zu AdminActions gibt es in der Django-Doku: https://docs.djangoproject.com/en/stable/ref/contrib/admin/actions/

Read Only Felder

Falls wir Felder in der Administrationsoberfläche readonly haben möchten, d.h., dass sie zumindest über die Admin nicht mehr geändert werden können, gibt es das Attribut readonly_fields. Wir wollen, dass der Autor eines Events nachträglich nicht mehr geändert werden kann und fügen folgendes in die Klasse ein:

readonly_fields = ("author",)

Damit sind alle Felder des Tuples auf readonly. Die fertige EventAdmin-Klasse sieht jetzt so aus:

@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
    list_display = (
        "date",
        "author",
        "name",
        "category",
    )
    list_display_links = ("name",)
    list_filter = ("category",)
    search_fields = ["name"]
    actions = ["make_active", "make_inactive"]
    readonly_fields = ("author",)
    radio_fields = {
        "category": admin.HORIZONTAL,
        "min_group": admin.VERTICAL,
    }

    @admin.action(description="Setze Events active")
    def make_active(self, request, queryset):
        """Set all Entries to active."""
        queryset.update(active=True)

    @admin.action(description="Setze Events inactive")
    def make_inactive(self, request, queryset):
        """Set all Entries to inactive."""
        queryset.update(active=False)

Fachmethoden in der Liste angeben

Bisher hatten wir in list_display nur gewöhnliche Felder angezeigt, zum Beispiel Name oder Autor. Was ist aber, wenn wir zum Beispiel für das Kategoriemodell in der Übersicht auch die Anzahl der Events anzeigen wollten?

Das Category - Model besitzt die methode num_of_events, welche wir im Zusammenhang mit Formularen für das Model angelegt hatten. Diese Methode liefert die Events einer Kategorie.

Wir erinnern uns:

class Category(DateMixin):
"""Eine Kategorie für einen Event."""

name = models.CharField(max_length=100, unique=True)
sub_title = models.CharField(max_length=200, null=True, blank=True)
description = models.TextField(null=True, blank=True)

class Meta:
    ordering = ["name"]

def __str__(self):
    return self.name

def num_of_events(self):
  """Die Anzahl der Events einer Kategorie."""
    return self.events.count()

Wir können diese Methode in list_display im Admin-Model genauso angeben, wie jedes beliebige andere Feld. Passen wir also die Klasse CategoryAdmin an:

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = "id", "name", "sub_title", "num_of_events"
    list_display_links = ("name", "sub_title")
    list_filter = ("events", "sub_title")
    search_fields = ["name"]
../_images/category_admin_events.png

Queryset überschreiben

Auf die Adminoberfläche haben alle User Zugriff, die den Staff-Status gesetzt haben. Wir könnten zum Beispiel implementieren, dass Adminuser, d.h. User mit dem Superuser-Status alle Events sehen können, alle anderen, zum Beispiel Moderatoren, nur eigene Events. Dafür lässt sich die Methode get_queryset() überschreiben.

def get_queryset(self, request):
    """Only superusers can see all events."""
    qs = super().get_queryset(request)
    if request.user.is_superuser:
        return qs
    return qs.filter(author=request.user)

Die Django-Adminoberfläche bietet noch vieles mehr, was angepasst werden und verändert werden kann. Das soll nur ein kurzer Einblick sein, was man mit relativ wenig Aufwand anpassen kann.

eine neue Djangoadmin Oberfläche

Wer eine schönere Djangoadmin-Oberfläche ohne viel Arbeit haben möchte, sollte sich mal Grapelli ansehen.

Damit lassen sich mit einfachen Mitteln deutliche Verbesserungen in der Optik erzielen.