Django Admin - Контент конкретного пользователя (администратора)
Я начинаю организовывать новый проект и скажу, что у меня будет несколько моделей, таких как продукты и каталоги.
Я позволю своим клиентам (а не посетителям, только конкретным клиентам) войти на сайт администратора Django для создания, редактирования и удаления своих собственных каталогов.
Скажем, я создаю модель под названием "Магазин" , создайте каждый магазин (имя, адрес, логотип, контактную информацию и т.д.) и создайте пользователя-администратора, связанного с этим магазином.
Теперь я хочу, чтобы этот новый администратор (кто не администратор сайта, а администратор магазина - вероятно, группа ), чтобы видеть и редактировать только каталоги, связанные с его магазином.
Возможно ли это?
Должен ли я делать это внутри администратора Django или мне нужно создать новое приложение "shop admin"?
Ответы
Ответ 1
Во-первых, предостерегающее предупреждение: философия дизайна admin в Django заключается в том, что любой пользователь, имеющий доступ к администратору (is_staff==True
), является доверенным пользователем, например. сотрудник, следовательно, "штат", чтобы получить доступ к администратору. Хотя вы можете настроить администратора для ограничения областей, позволяя любому, кто не входит в вашу организацию, получить доступ к вашему администратору, считается рискованным, и Django не дает никаких гарантий относительно какой-либо безопасности в этот момент.
Теперь, если вы все еще хотите продолжить, вы можете ограничить большинство вещей, кроме магазинов сразу с места в карьер, просто не присваивая этим привилегии пользователю. Вам нужно будет предоставить всем владельцам магазинов права на редактирование любых моделей магазинов, к которым им нужен доступ, но все остальное должно быть оставлено в списке разрешений.
Затем для каждой модели, которая должна быть ограничена только глазами владельца, вам нужно будет добавить поле для хранения "владельца" или разрешить пользователю доступ к нему. Вы можете сделать это с помощью метода save_model
на ModelAdmin
, который имеет доступ к объекту запроса:
class MyModelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super(MyModelAdmin, self).save_model(request, obj, form, change)
Затем вам также необходимо ограничить набор запросов ModelAdmin только теми элементами, которые принадлежат текущему пользователю:
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super(MyModelAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
Однако это ограничит только то, что попадает в список, пользователь все равно может играть с URL-адресом для доступа к другим объектам, к которым у них нет доступа, поэтому вам необходимо переопределить каждый из уязвимых представлений ModelAdmin для перенаправления, если пользователь не является владельцем:
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
class MyModelAdmin(admin.ModelAdmin):
def change_view(self, request, object_id, form_url='', extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).change_view(request, object_id, form_url, extra_context)
def delete_view(self, request, object_id, extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).delete_view(request, object_id, extra_context)
def history_view(self, request, object_id, extra_context=None):
if not self.queryset(request).filter(id=object_id).exists():
return HttpResponseRedirect(reverse('admin:myapp_mymodel_changelist'))
return super(MyModelAdmin, self).history_view(request, object_id, extra_context)
ОБНОВЛЕНИЕ 06/05/12
Спасибо @christophe31 за указание, что поскольку запрос запроса ModelAdmin
уже ограничен пользователем, вы можете просто использовать self.queryset()
в просмотрах изменений, удаления и истории. Это прекрасно абстрагирует модельное имя класса, делая код менее хрупким. Я также изменил использование filter
и exists
вместо блока try...except
с помощью get
. Он более оптимизирован таким образом и на самом деле приводит к более простому запросу.
Ответ 2
Я просто размещаю это здесь, так как верхний комментарий больше не является самым последним ответом. Я использую Django 1.9, я не уверен, когда это произошло.
Например, у вас разные места и разные пользователи, связанные с каждым местом, модель будет выглядеть примерно так:
class Venue(models.Model):
user = models.ForeignKey(User)
venue_name = models.CharField(max_length=255)
area = models.CharField(max_length=255)
Теперь статус сотрудника для пользователя должен быть правдой, если он разрешил вход через панель администратора django.
admin.py выглядит примерно так:
class FilterUserAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if getattr(obj, 'user', None) is None:
obj.user = request.user
obj.save()
def get_queryset(self, request):
qs = super(FilterUserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(user=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
return True
return obj.user == request.user or request.user.is_superuser
@admin.register(Venue)
class VenueAdmin(admin.ModelAdmin):
pass
имя функции изменилось с queryset на get_queryset.
EDIT: Я хотел бы продлить мой ответ. Существует еще один способ возврата отфильтрованных объектов без использования функции запроса. Я хочу подчеркнуть, что я не знаю, эффективен ли этот метод или менее эффективен.
Альтернативная реализация метода get_queryset выглядит следующим образом:
def get_queryset(self, request):
if request.user.is_superuser:
return Venue.objects.all()
else:
return Venue.objects.filter(user=request.user)
Кроме того, мы также можем фильтровать контент, отношения более глубокие.
class VenueDetails(models.Model):
venue = models.ForeignKey(Venue)
details = models.TextField()
Теперь, если я хочу отфильтровать эту модель, которая имеет место как foreignkey, но не имеет пользователя, мой запрос изменяется следующим образом:
def get_queryset(self, request):
if request.user.is_superuser:
return VenueDetails.objects.all()
else:
return VenueDetails.objects.filter(venue__user=request.user)
Django ORM позволяет нам обращаться к различным типам отношений, которые могут быть настолько глубокими, насколько мы хотим, используя '__'
Ниже приведена ссылка на официальные документы для вышеуказанного.
Ответ 3
Прошу прощения, я знаю, что поздно, но, возможно, это поможет кому-то еще. Я думаю, что приложение django-permission могло бы помочь выполнить эту задачу.
Ответ 4
Я думаю, что RelatedOnlyFieldListFilter должен вам помочь.
Здесь ссылка на Django Doc: RelatedOnlyFieldListFilter
list_filter может быть: кортеж, где первый элемент является полем имя и второй элемент - это класс, наследующий от django.contrib.admin.FieldListFilter, например:
class PersonAdmin(admin.ModelAdmin):
list_filter = (
('is_staff', admin.BooleanFieldListFilter),
)
Вы можете ограничить выбор связанной модели объектами участвуя в этом отношении, используя RelatedOnlyFieldListFilter: (Vous pouvez limiter les choix dun modèle lié aux objets озабоченность par la Отношение en utilisant RelatedOnlyFieldListFilter:)
class BookAdmin(admin.ModelAdmin):
list_filter = (
('author', admin.RelatedOnlyFieldListFilter),
)
Предполагая, что автор является моделью ForeignKey для пользователя, , это ограничит выбор list_filter для пользователей, которые написали книгу вместо перечисления всех пользователей. (En supposant que author est une clé ForeignKey vers un modèle Пользователь, cela va limiter les choix de list_filter aux utilisateurs qui ont écrit un livre au lieu dénumérer tous les utilisateurs.)