Django Admin - Конкретный пользовательский (админский) контент - PullRequest
46 голосов
/ 10 июня 2011

Я начинаю организовывать новый проект и, скажем, у меня будет несколько моделей, таких как продукты и каталоги .

Я разрешу своим клиентам (не посетителям, а только конкретным клиентам) входить на сайт администратора Django для создания, редактирования и удаления своих собственных каталогов.

Допустим, я создаю модель с именем "Магазин" , создаю каждый магазин (название, адрес, логотип, контактные данные и т. Д.) И создаю администратора, привязанного к этому магазину.

Теперь я хочу, чтобы этот новый администратор (не администратор сайта, а администратор магазина - вероятно, группа пользователей ) мог видеть и редактировать только каталоги, связанные с его магазином .

Это возможно?

Должен ли я сделать это внутри администратора Django или создать новое приложение "администратора магазина"?

Ответы [ 4 ]

58 голосов
/ 11 июня 2011

Во-первых, предостерегающее предупреждение: философия проектирования администратора 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.Таким образом, он более упорядочен и на самом деле приводит к более простому запросу.

12 голосов
/ 22 ноября 2016

Я просто публикую это здесь, так как верхний комментарий больше не самый актуальный ответ. Я использую 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.

РЕДАКТИРОВАТЬ: Я хотел расширить свой ответ. Есть еще один способ вернуть отфильтрованные объекты без использования функции queryset. Я хочу подчеркнуть, что я не знаю, является ли этот метод более эффективным или менее эффективным.

Альтернативная реализация метода 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()

Теперь, если я хочу отфильтровать эту модель, в которой Venue имеет значение 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 позволяет нам получать доступ к различным типам отношений, которые могут быть настолько глубокими, насколько мы хотим, через '__'

Вот ссылка на официальные документы для вышеупомянутого.

0 голосов
/ 18 ноября 2017

Я думаю, что RelatedOnlyFieldListFilter должен вам помочь. Вот ссылка на Django Doc: RelatedOnlyFieldListFilter

list_filter может быть: кортеж, где первый элемент является полем имя и второй элемент является классом, наследующим от django.contrib.admin.FieldListFilter, например:

class PersonAdmin(admin.ModelAdmin):
    list_filter = (
        ('is_staff', admin.BooleanFieldListFilter),
    )

Вы можете ограничить выбор связанной модели объектами. участвует в этом отношении, используя RelatedOnlyFieldListFilter: (Vous Pouvez Limer Les Choix D'un Modlele LUX AUX ОБЪЕКТЫ концерны пар отношение к утилитарному делу RelatedOnlyFieldListFilter:)

  class BookAdmin(admin.ModelAdmin):
      list_filter = (
           ('author', admin.RelatedOnlyFieldListFilter),
      ) 

Предполагая, что автор является ForeignKey для модели User, это ограничит выбор list_filter пользователями, которые написали книгу вместо перечисления всех пользователей . (И я полагаю, что автор не имеет смысла ForeignKey и un modèle Пользователь, ограниченный список пользователей list_filter aux utilisateurs qui on ecrit un livre au lieu d’énumérer Tous Les Utilisateurs.)

0 голосов
/ 25 октября 2016

Извините, я знаю, что уже поздно, но, возможно, это поможет кому-то еще. Я думаю, что приложение django-Разрешение может помочь в достижении этой цели.

...