Подтвердить что ты не робот

Django Admin - Контент конкретного пользователя (администратора)

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

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

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

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

Возможно ли это?

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

4b9b3361

Ответ 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.)