Pull to refresh

Доступ только к собственным записям из Django админки

Lumber room
Awaiting invitation
Иногда нужно, чтобы каждый пользователь админки Django мог видеть и редактировать только свои записи. По умолчанию такая возможность не доступна, но с помощью нехитрых действий можно всё исправить.

Суть подхода заключается в добавлении к нашей модели внешнего ключа на модель пользователя Django, а затем переопределение некоторых методов класса, отвечающего за генерацию админки.

Администратор сайта видит все записи и может менять их владельцев (или создавать объекты от чужого имени). Остальные пользователи, имеющие доступ в панель администрирования, могут создавать и редактировать только свои записи.

models.py


# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User

class Entry(models.Model):
   user = models.ForeignKey(User, verbose_name=u"пользователь", blank=True, null=True)
   title = models.CharField(u"заголовок", max_length=100)

   def __unicode__(self):
      return self.title

   class Meta:
      verbose_name = u"""запись"""
      verbose_name_plural = u"""записи"""


Обратите внимание на атрибуты blank и null у поля user.

Так как поле будет заполняться автоматически, то нужно разрешить Django оставлять его пустым (blank=True).

null=True необходим для корректной работы нашего кода. Вообще, поле всегда будет содержать id пользователя и было бы правильно указать null=False, но из-за внутреннего устройства Django такой подход не пройдет.

admin.py


# -*- coding: utf-8 -*-
from django.contrib import admin
from app.models import Entry

class EntryAdmin(admin.ModelAdmin):

   # Поля, доступные для редактирования простым пользователям.
   # Разрешаем только title
   user_fieldsets = (
     (None, {
         'classes': ('wide',),
         'fields': ('title',)
      }),
   )

   list_display = ['title', 'user']
   raw_id_list_displayfields = ('user',)
   search_fields = ['title', 'user__username']

   def save_model(self, request, obj, form, change):
      if form.is_valid():
         if not request.user.is_superuser or not form.cleaned_data["user"]:
            obj.user = request.user
            obj.save()
         elif form.cleaned_data["user"]:
            obj.user = form.cleaned_data["user"]
            obj.save()

   def preprocess_list_display(self, request):
      if 'user' not in self.list_display:
         self.list_display.insert(self.list_display.__len__(), 'user')
      if not request.user.is_superuser:
         if 'user' in self.list_display:
            self.list_display.remove('user')

   def preprocess_search_fields(self, request):
      if 'user__username' not in self.search_fields:
         self.search_fields.insert(self.search_fields.__len__(), 'user__username')
      if not request.user.is_superuser:
         if 'user__username' in self.search_fields:
            self.search_fields.remove('user__username')

   def changelist_view(self, request, extra_context=None):
      self.preprocess_list_display(request)
      self.preprocess_search_fields(request)
      return super(EntryAdmin, self).changelist_view(request)

   def queryset(self, request):
      if request.user.is_superuser:
         return super(EntryAdmin, self).queryset(request)
      else:
         qs = super(EntryAdmin, self).queryset(request)
         return qs.filter(user=request.user)

   def get_fieldsets(self, request, obj=None):
      if request.user.is_superuser:
         return super(EntryAdmin, self).get_fieldsets(request, obj)
      return self.user_fieldsets

admin.site.register(Entry, EntryAdmin)

Разберемся с кодом.

Метод save_model отвечает за автозаполнение поля user. Несколько несложных условий подставляют необходимый id пользователя.

Метод preprocess_list_display добавляет колонку user в список объектов, если его просматривает администратор и удаляет колонку для обычных пользователей. Обратите внимание, что сперва мы проверяем, есть ли запись в list_display и если её нет, то добавляем, а потом уже смотрим кто просматривает список.
При создании экземпляра EntryAdmin все его поля становятся глобальными для процесса, поэтому если мы удалим колонку list_display для обычного пользователя и не добавим для администратора, то администратор колонку не увидит. Именно поэтому мы каждый раз её вставляем в список.

Метод preprocess_search_fields делает тоже, что и preprocess_list_display, но только для search_fields. Фактически мы дали возможность администратору не только видеть автора записи, но и осуществлять поиск по его username.

Оба предыдущих метода вызываются из переопределенного changelist_view.

Метод queryset возвращает все записи для администратора и персональные записи для обычных пользователей. При изменении записи, обращение к ней идет через этот самый queryset, поэтому если пользователь пытается напрямую получить доступ к чужому объекту (введет в строку браузера что-то вроде /admin/app/entry/3/), то ему вернется HTTP 404.

Метод get_fieldsets подключает user_fieldsets в случае обычных пользователей или же генерирует fieldsets на основе данных модели Entry.

Вот и все, осталось только назначить пользователям необходимые права «can add entry», «can change entry» и т.д. А также поставить галочку напротив поля «Статус персонала» на странице редактирования данных пользователя. «Статус персонала» разрешит человеку входить в административную часть сайта.
Tags:
Hubs:
You can’t comment this post because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.