Spass mit Newforms-Admin - Automatische Felder

Geschrieben 3 Monate, 1 Woche zuvor

Im letzten Artikel Rowlevel-Permissions ging es darum, wie man Einträge nur vom Benutzer selber bearbeiten lassen kann. Wer den Artikel verfolgt und aufgebaut hat, wird festgestellt haben, dass die ganze Sache einen gewaltigen Haken hatte:

In der Detailansicht konnte das Autorenfeld frei definiert werden, sprich Benutzer A konnte sich für Benutzer B ausgeben und umgekehrt. In diesem Artikel geht es darum, dem eingeloggten Autor automatisch den Artikel zuzuweisen.

Holzhammer

Die einfachste Lösung ist: Zeige das Autorenfeld gar nicht erst an und weise beim Speichern des Artikels dem Autorenfeld den eingeloggten User zu. Das Ganze ist schnell geschehen. Ähnlich wie die "newforms save()" Methode stellt auch ModelAdmin eine Methode zum Überschreiben zur Verfügung:

class EntryAdmin(admin.ModelAdmin):

    # Autorenfeld ausblenden (mit #7937 geht das irgendwann mal einfacher)
    # #7937: http://code.djangoproject.com/ticket/7973
    fields = (
        'title',
        'content',
    )

    # Formular valdidieren, Autor setzen und speichern
    def save_form(self, request, form, change):
        instance = form.save(commit=False)
        # Nur bei neuen Artikeln den User setzen
        if not change:
            instance.author = request.user
        return instance

Fertig ist die ganze Geschichte. Schnell, sicher und kaum zu überlisten.

Nicht ganz so krass

Vielleicht ist das aber auch schon einen Schritt zu weit. In meinem aktuellen Projekt möchte ich es trotzdem dem Benutzer überlassen, den Autor zu setzen, allerdings mit einer Kleinigkeit: Der aktuelle Benutzer soll in der Liste schon vorausgewählt sein.

Das Ganze ist ein wenig mehr "tricky". In ModelAdmin gibt es wiederrum eine Methode, die vor der Anzeige der Felder aufgerufen wird und mit der man ihr Verhalten und Aussehen (Widgets) beeinflussen kann: formfield_for_dbfield.

class EntryAdmin(admin.ModelAdmin):

    def formfield_for_dbfield(self, db_field, **kwargs):
        field = super(EntryAdmin, self).formfield_for_dbfield(db_field, **kwargs)

        # Autorenfeld hat als Vorauswahl den aktuellen User
        if db_field.name == "author":
            field.initial = request.user.pk
        return field

In dieser Methode prüfen wir, ob das aktuelle Feld unser "Autorenfeld" ist, dann wird ihm als Startwert (initial) der aktuelle User zugewiesen.

Nächster Haken: Der obige Code funktioniert nicht, in der Methode formfield_for_dbfield hat man keinen Zugriff auf das request-Objekt. Ein wenig Patching muss her:

class EntryAdmin(admin.ModelAdmin):

    def add_view(self, request,  *args, **kwargs):
        self._request = request
        return super(EntryAdmin, self).add_view(request,  *args, **kwargs)

    def change_view(self, request, *args, **kwargs):
        self._request = request
        return super(EntryAdmin, self).change_view(request,  *args, **kwargs)

    def formfield_for_dbfield(self, db_field, **kwargs):
        field = super(EntryAdmin, self).formfield_for_dbfield(db_field, **kwargs)

        # Autorenfeld hat als Vorauswahl den aktuellen User
        if db_field.name == "author":
            field.initial = self._request.user.pk
        return field

Glücklicherweise ist ModelAdmin flexibel genug, das Problem zu lösen. Die Methoden add_view und change_view werden vor der Anzeige des Formulares aufgerufen und beiden steht das Request-Objekt zur Verfügung. Das ist genau der richtige Ort, das Request-Objekt als globale Eigenschaft (self._request) der Klasse zuzuweisen.

Somit kann man in der formfield-Methode einfach auf das "globale" Request-Objekt zugreifen und mit den paar Handgriffen ist der Benutzer in der Autorenliste vorausgewählt.

Mit ein wenig Javascript kann man auch einfach aus diesen beiden Sachen eins machen:

Und nicht vergessen: Auch hier ist es wieder möglich mit Permissions zu arbeiten. So könnte ein Administrator immer den Autor setzen (zweite Variante), wo ein "normaler" User kein Autorenfeld zu Gesicht bekommt und automatisch gesetzt wird (erste Variante).


Bisher wurden 4 Kommentare hinterlassen

Martin Geber

Das Video ist schön... Wo ist das JS? Smiley:  :P Ja, ja ich zerbreche mir selbst den Kopf...

Wiedereinmal ein spitze Artikel. Danke. (Mir war auch nicht bewusst, dass ich endlich auf request zugreifen kann.)

*nun threadlocals endlich weg werfen kann*

Immer weiter so.

Pascal

Wunderbar! Habs direkt bei mir eingebaut -- und das JS in jQuery nachgebaut: http://gist.github.com/7323

Max Meier

wie wuerde das denn konkret Ausschauen mit der Kombination von beidem, also Admin bekommt Feld zur auswahl und normaler User nicht?

Max Meier

so gehts, enthaelt aber leider viel redundanten code:

from django.contrib import admin
from mysite.weblog.models import Entry

class EntryAdmin(admin.ModelAdmin):

    list_display = (
        'title',
        'author',
    )

    # validate form, set user if not super user 
    def save_form(self, request, form, change):

        # set the author field automatically if not correct permissions
        if not request.user.has_perm('weblog.can_view_all'):

            instance = form.save(commit=False)

            # set user only for new entries 
            if not change:
                instance.author = request.user

            return instance
        else:
            return super(EntryAdmin, self).save_form(request, form, change)

    def add_view(self, request,  *args, **kwargs):

        # hide the author field from normal users
        if not request.user.has_perm('weblog.can_view_all'):
            self.fields = (
                'title',
                'content',
            )

        self._request = request
        return super(EntryAdmin, self).add_view(request,  *args, **kwargs)


    def change_view(self, request, *args, **kwargs):

        #hide the author field from normal users
        if not request.user.has_perm('weblog.can_view_all'):
            self.fields = (
                'title',
                'content',
            )

        self._request = request
        return super(EntryAdmin, self).change_view(request,  *args, **kwargs)

    def formfield_for_dbfield(self, db_field, **kwargs):
        field = super(EntryAdmin, self).formfield_for_dbfield(db_field, **kwargs)

        # Autorenfeld hat als Vorauswahl den aktuellen User
        if db_field.name == "author":
            field.initial = self._request.user.pk
        return field

    # show only author's own entries as long as you don't have more permissions
    def queryset(self, request):
        if request.user.has_perm('weblog.can_view_all'):
            return self.model._default_manager.get_query_set()
        return self.model._default_manager.filter(author=request.user)

admin.site.register(Entry, EntryAdmin)

Du bist hier: mahner.org » Weblog » Django & Python » Spass mit Newforms-Admin - Automatische Felder

↑ Nach oben

© 2001–2007 Martin Mahner. The content is licensed under (cc) creative commons.
This site is powered by Django. Hosted by manitu.

Generated: Mo, 1 Dez 2008 18:59:12 +0100