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.
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.
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).
© 2001—2009 Martin Mahner. This is an I ♥ Django Project.
Admin | Generated: Sat, 4 Jul 2009 09:02:07 +0200
Reply in this thread Martin Geber Aug. 26, 2008
Das Video ist schön... Wo ist das JS? :P Ja, ja ich zerbreche mir selbst den Kopf...
Wiedereinmal ein spitze Artikel. Danke. (Mir war auch nicht bewusst, dass ich endlich auf
requestzugreifen kann.)*nun threadlocals endlich weg werfen kann*
Immer weiter so.
Reply in this thread Pascal Aug. 26, 2008
Wunderbar! Habs direkt bei mir eingebaut -- und das JS in jQuery nachgebaut: http://gist.github.com/7323
Reply in this thread Max Meier Sept. 4, 2008
wie wuerde das denn konkret Ausschauen mit der Kombination von beidem, also Admin bekommt Feld zur auswahl und normaler User nicht?
Reply in this thread Max Meier Sept. 4, 2008
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)