.. _admin_app: .. index:: single: admin single: Administrationsoberfläche 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 Kategorie des Events in einem Drop-Down-Feld auswählen. Wir wollen dies ändern und aus dem Drop-Down-Feld ein Reihe von Radio-Buttons machen, die horizontal angeordnet sind. Zusätzlich wollen wir das Min-Group-Feld als vertikal angeordnete Radio-Group anlegen. Choice-Felder als Radio Buttons in der Admin darstellen ====================================================================== Wir öffnen die Datei ``event_manager/events/admin.py`` und modifizieren das ``EventAdmin``-Klasse: .. code-block:: python @admin.register(Event) class EventAdmin(admin.ModelAdmin): # anderer Code radio_fields = { "category": admin.HORIZONTAL, "min_group": admin.VERTICAL, } Die Eigenschaft ``radio_fields`` der ModelAdmin-Klasse ermöglicht uns kompfortabel das Ändern der beiden Eigenschaften in Radiofields. 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``. 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: .. code-block:: python @admin.register(Event) class EventAdmin(admin.ModelAdmin): # ... mehr Code list_filter = ("category",) list_display = "date", "slug", "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. .. code-block:: python @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. .. code-block:: python actions = ["make_active", "make_inactive"] Mehr zu AdminActions gibt es in der Django-Doku: ``_ Den Kategorie-Slug in der Event-Übersicht darstellen ====================================================================== Bisher zeigen wir nur die Kategorie in der Eventübesicht an. Wir wollen die Ansicht aber erweitern, und den Slug der ``Category`` auch noch darstellen. Leider funktioniert der Zugriff via ``category__slug`` in der ``list_display``-Eigenschaft nicht. Es kommt zu einem Fehler .. code-block:: python @admin.register(Event) class EventAdmin(admin.ModelAdmin): # ... mehr Code list_display = ("date", "slug", "author", "name", "category", "category__slug") Um das Problem zu umgehen, schreiben wir uns eine eigene Methode, dekorieren sie mit dem ``@admin.display``-Dekorator und nutzen den Methoden-Namen in ``list_display``, um die Spalte anzuzeigen. .. code-block:: python @admin.register(Event) class EventAdmin(admin.ModelAdmin): # mehr Code list_display = ("date", "slug", "author", "name", "category", "category_slug") @admin.display(description="Kategorie Slug") def category_slug(self, obj): return obj.category.slug Wir können problemlos eigene Methoden in der ``EventAdmin``-Klasse definieren, und diese als Felder in der Übersicht unserer Events darstellen. Das übergebene ``obj`` ist im Fall von ``category_slug`` das Event-Objekt selbst. 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: .. code-block:: python readonly_fields = ("author",) Damit sind alle Felder des Tuples auf readonly. Die fertige ``EventAdmin``-Klasse sieht jetzt so aus: .. code-block:: python @admin.register(Event) class EventAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("name",)} list_display = ( "date", "slug", "author", "name", "category", "category_slug", ) list_filter = ("category",) search_fields = ["name"] actions = ["make_active", "make_inactive"] # date_hierachy = "date" readonly_fields = ("author",) list_display_links = ("name", "slug") 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) @admin.display(description="Kategorie Slug") def category_slug(self, obj): return obj.category.slug