如何使用Django Rest Framework视图集按父对象的属性过滤嵌套数据

时间:2019-01-22 13:06:04

标签: django permissions django-rest-framework nested

我有嵌套数据;列表包含许多项目。为了安全起见,我通过当前用户是否创建列表以及列表是否公开来过滤列表。我想对商品执行相同的操作,以便商品只能由经过身份验证的用户更新,如果列表是公开的,则任何人都可以查看。

这是我的视图集代码,从工作正常的列表视图集代码改编而成。这当然不适用于Item,因为该项目不具有“ created_by”或“ is_public”属性-这些是父级列表的属性。

有没有办法用列表属性替换“ created_by”和“ is_public”?即可以在项目的get_queryset方法中掌握父级列表对象,并检查其属性吗?

另一种选择是,我也为该项目分配了“ created_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()

非常感谢您的帮助!

编辑:在卢卡斯·韦恩的答案和this post之间,我认为我已经对此进行了整理。这是我在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 :(得分:1)

Django允许lookups that span relationships。您可以在列表属性中过滤Item对象,只需在模型之间使用相关字段的字段名,并用双下划线分隔,直到找到所需的字段。

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)

要只允许项目所有者更新项目,请写一个custom object-level permission class

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