Как отфильтровать вложенные данные по свойству родительского объекта, используя набор представлений Django Rest Framework - PullRequest
0 голосов
/ 22 января 2019

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

Вот мой код набора представлений, адаптированный из кода набора списков, который работает нормально. Это, конечно, не работает для элементов, потому что элемент не имеет свойств «create_by» или «is_public» - это свойства родительского списка.

Есть ли способ, которым я могу заменить "creat_by" и "is_public" на свойства списка? я могу получить объект родительского списка в методе get_queryset элемента и проверить его свойства?

Альтернативой является то, что я также назначаю элементу "create_by" и "is_public", но я бы предпочел этого не делать, потому что это дублированные данные. Свойства списков должны контролировать права доступа элемента.

class ItemViewSet(viewsets.ModelViewSet):
    permission_classes = [permissions.AllowAny, ]
    model = Item
    serializer_class = ItemSerializer

    def get_queryset(self):
        # restrict any method that can alter a record
        restricted_methods = ['POST', 'PUT', 'PATCH', 'DELETE']
        if self.request.method in restricted_methods:
            # if you are not logged in you cannot modify any list
            if not self.request.user.is_authenticated:
              return Item.objects.none()

            # you can only modify your own lists
            # only a logged-in user can create a list and view the returned data
            return Item.objects.filter(created_by=self.request.user)

        # GET method (view item) is available to owner and for items in public lists
        if self.request.method == 'GET':
          if not self.request.user.is_authenticated:
            return Item.objects.filter(is_public__exact=True)

          return Item.objects.filter(Q(created_by=self.request.user) | Q(is_public__exact=True))

        # explicitly refuse any non-handled methods
        return Item.objects.none()

Большое спасибо за любую помощь!

Редактировать: между ответом Лукаса Вейна и этой записью Я думаю, что теперь у меня есть сортировка Вот мой рабочий код в api.py:

from rest_framework import viewsets, permissions
from .models import List, Item
from .serializers import ListSerializer, ItemSerializer
from django.db.models import Q


class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # handle permissions based on method
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        if hasattr(obj, 'created_by'):
            return obj.created_by == request.user

        if hasattr(obj, 'list'):
            if hasattr(obj.list, 'created_by'):
                return obj.list.created_by == request.user

class ListViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadOnly]
    model = List
    serializer_class = ListSerializer

    def get_queryset(self):
        # can view public lists and lists the user created
        if self.request.user.is_authenticated:
            return List.objects.filter(
                Q(created_by=self.request.user) | 
                Q(is_public=True)
            )

        return List.objects.filter(is_public=True)

    def pre_save(self, obj):
        obj.created_by = self.request.user

class ItemViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadOnly]
    model = Item
    serializer_class = ItemSerializer

    def get_queryset(self):
        # can view items belonging to public lists and lists the usesr created
        if self.request.user.is_authenticated:
            return Item.objects.filter(
                Q(list__created_by=self.request.user) | 
                Q(list__is_public=True)
            )

        return Item.objects.filter(list__is_public=True)

1 Ответ

0 голосов
/ 24 января 2019

Django позволяет поисков, которые охватывают отношения . Вы можете фильтровать объекты Item по свойствам List, просто используйте имя поля связанных полей в моделях, разделенных двойным подчеркиванием, пока не доберетесь до нужного поля.

class ItemViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadyOnly]
    serializer_class = ItemSerializer

    def get_queryset(self):
        if self.request.user.is_authenticated
            return Item.objects.filter(
                Q(list__created_by=self.request.user) | 
                Q(list__is_public__exact=True)
            )

        return Item.objects.filter(list__is_public=True)

Чтобы разрешить обновление элементов только их владельцам, напишите пользовательский класс разрешений на уровне объекта .

class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # Read permissions are allowed to any request,
        # so we'll always allow GET, HEAD or OPTIONS requests.
        if request.method in permissions.SAFE_METHODS:
            return True

        # Instance must have an attribute named `created_by`.
        return obj.list.created_by == request.user
...