1

我有嵌套数据;一个列表包含许多项目。为了安全起见,我根据当前用户是否创建列表以及列表是否公开来过滤列表。我想对项目做同样的事情,以便项目只能由经过身份验证的用户更新,但如果列表是公开的,任何人都可以查看。

这是我的视图集代码,改编自工作正常的列表视图集代码。这当然不适用于项目,因为项目没有属性“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()

非常感谢您的帮助!

编辑:在 Lucas Weyne 的回答和这篇文章之间,我想我现在已经整理好了。这是我在 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)
4

1 回答 1

1

Django 允许跨关系的查找。您可以跨 List 属性过滤 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)

要允许项目仅由其所有者更新,请编写自定义对象级权限类

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
于 2019-01-24T11:06:37.943 回答