Spass mit Newforms-Admin - Rowlevel-Permissions

Newforms-Admin ist schon eine Weile verfügbar und daher wird es Zeit, sich einmal mit den Features auseinander zu setzen. Dieser Artikel ist der Anfang einer Reihe von Tipps und Tricks zu Newforms-Admin, die nacheinander alle aufeinander aufbauen.

Ein oft gewünschtes Feature im Admin-Interface ist "Lasse den Benutzer nur seine eigenen Einträge bearbeiten", anders ausgedrückt: Rowlevel-Permissions. Eine der einfachsten Aufgaben für das neue Admin-Interface.

Fangen wir wieder einmal mit dem Model an, ein kleines Weblog ist ein gutes Beispiel:

from django.db import models
from django.contrib.auth.models import User

class Entry(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    author = models.ForeignKey(User)

    def __unicode__(self):
        return self.title

Um das Model mit dem Admin Interface administrieren zu können, ist eine einfache ModelAdmin-Klasse notwendig, bestens aufgehoben in der Datei admin.py (Jahaa):

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

class EntryAdmin(admin.ModelAdmin):
    list_display = (
        'title',
        'author',
    )

admin.site.register(Entry, EntryAdmin)

Soweit nichts spektakuläres, mit ein paar Einträgen von verschiedenen Usern sieht die Übersichtsseite so aus:

Nun zur Aufgabe: Um dem Benutzer nur seine Beiträge bearbeiten zu lassen, lässt Newforms-Admin auf einfache Weise zu, das Queryset für die Listenansicht zu bearbeiten. Füge in deine ModelAdmin-Klasse eine Funktion "queryset" hinzu, die die Einträge anhand der User/Autor-Beziehung filtert:

class EntryAdmin(admin.ModelAdmin):
    # ...
    def queryset(self, request):
        return self.model._default_manager.filter(author=request.user)

Das war es auch schon! Jeder Autor sieht ab sofort nur noch seine eigenen Artikel:

Die Sache hat nur einen Haken: Auch Administratoren würden nur ihre eigenen Artikel sehen, irgendwie nicht sehr sinnvoll. Um das Problem zu lösen, schaffen wir uns am einfachsten eine zusätzliche Permission. Erweitere also dein Model wie folgt:

class Entry(models.Model):
    # ...
    class Meta:
        permissions = (
            ('can_view_all', 'Can view all Entries'),
        )

Nach einem "./manage.py syncdb" ist diese neue Permission auch schon verfügbar, die du deinem User zuweisen kannst:

Leider ist nicht genug Voodoo in Django vorhanden, als dass alles automatisch geht. In deiner Queryset-Redefinition musst du noch auf die neue Permission prüfen, etwa so:

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)

Und fertig. "Normale" User können nun nur noch ihre eigenen Beiträge bearbeiten während Administratoren oder User mit der Permission "can_view_all" alle Einträge angezeigt bekommen.

Zu guter letzt noch ein Hinweis: Theoretisch könnte man dieses Verhalten auch umdrehen und eine Permission "Can view only his own entries" (*urgs*) schaffen. Das nur fürs Gedächtnis.


  • Martin Geber Aug. 26, 2008

    Hallo,

    super Beitrag. Danke.

    @Luis: Das muss man soweit ich weiß nicht, da diese Abfrage: request.user.has_perm('weblog.can_view_all') bei Superusern immer True zurück gibt. Das wird also damit sowieso schon abgefragt.

    Noch einmal ein dickes danke in Martins Richtung. Sehr gut verständlich.

    Viele Grüße.

  • Martin Aug. 26, 2008

    Genau Martin (Geber), Superuser besitzen automatisch alle (!) Rechte, Luis schau auch mal ins Bild ;)

    Wenn man den Spieß nun umdreht und eine Permission setzt alá "Kann nur eigene Artikel sehen", dann müsste man auch auf Superuser-Status prüfen.

  • Luis Aug. 27, 2008

    wohl wahr wohl wahr... =)

  • Thomas Güttler Sept. 30, 2008

    Leider funktioniert dieses Vorgehen nicht wirklich.

    Die Liste der Objekte wird zwar im Admin-Interface mit der queryset Methode gefiltert. Aber mehr passiert nicht. Man kann also oben in der URL einfach die ID eines anderen Eintrags nehmen!

    Man müsste also auch die save() Methode überschreiben. Aber in dessen Kontext ist leider das Request Objekt (inkl. request.user) nicht vorhanden.

    PS: Das Email-Feld für Blog Kommentare ist zu klein. Eine Emailadresse kann mehr Zeichen beinhalten.

  • Martin Oct. 6, 2008

    @thomas: die Sache mit "id in der url ändern" hab ich schlichtweg noch gar nicht probiert. Danke für den Tipp.

    Die Save-Methode in ModelAdmin hat ein request-Objekt. schau mal auf Automatische Felder oben bei Holzhammer.

    E-Mail-Feld, stimmt, 30 Zeichen sind vielleicht ein wenig mager, ich hab es jetzt mal auf 80 gestellt.


Comments closed

Sorry, new comments are no longer allowed for this entry.

Write me an email if you have feedback or any questions regarding this post. If you found this post useful and just want to say thank you then don't forget that I have an Amazon Wishlist. :-)


↑ to the elevators

© 2001—2010 Martin Mahner. This is an I ♥ Django Project.

Admin | Generated: Sun, 25 Jul 2010 17:05:42 +0200