【问题标题】:Handle Django Rest Framework permission when creating related objects创建相关对象时处理 Django Rest Framework 权限
【发布时间】:2022-09-23 01:43:28
【问题描述】:

DRF 上的 Permission 的 has_object_permission 方法显然不会在 Create 上执行,因为该对象还不存在。但是,在某些用例中,权限取决于相关对象。例如:

class Daddy(models.Model):
    name = models.CharField(max_length=20)
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)


class Kiddy:
    title = models.CharField(max_length=12)
    daddy = models.ForeignKey(Daddy, on_delete=models.CASCADE)

如果我们只想允许Daddy 的所有者创建KiddyDaddy,我们将不得不在某个地方验证它。

我知道这是一个非常普遍的讨论,在this question 和更多其他地方也提到过。它还在DRF GitHub itself 上进行了讨论,其中为此目的进行了文档更新,并参考 DRF 文档解决了问题here with the following sentence

...为了限制对象的创建,您需要在您的 Serializer 类中实现权限检查或覆盖 ViewSet 类的 perform_create() 方法。

因此,参考 DRF 文档,我们可以执行以下解决方案之一:

class KiddySerializer(viewsets.ModelViewSet):
    validate_daddy(self, daddy):
        if not daddy.owner == self.context[\'request\'].user:
            raise ValidationError(\"You cannot create Kiddies of that Daddy\")

        return daddy

或者

class KiddyViewSet(ModelViewSet):
    def perform_create(self, serializer):
        if (self.request.user != serializer.validated_data[\'daddy\'].owner)
            raise ValidationError(\"You cannot create Kiddies of that Daddy\")
        
        serializer.save()

现在,有一个问题也提出了我的问题。如果我关心在未经授权的请求下共享给用户的信息怎么办。因此,在Daddy 不存在的情况下,用户将获得: Invalid pk \\\"11\\\" - object does not exist 并且在对象存在但用户无权访问的情况下,它将返回You cannot create Kiddies of that Daddy

我想在两种情况下都显示相同的消息:

The Daddy does not exist or you don\'t have permission to use it.

如果我使用如下 PermissionClass 是可能的:

class OwnsDaddy(BasePermission):
    def has_permission(self, request, view):
        if not Daddy.objects.allowed_daddies(request.user).filter(pk=request.data[\'daddy\']).exists():
            return False

这也可以,但是由于权限是在序列化程序之前验证的,如果用户传递的 daddy 的 ID 不正确(比如说字符串),就会导致 500 错误。我们可以通过 try-except 子句来防止这种情况,但仍然感觉不对。

所以,最后。什么是解决这个问题的好方法?

    标签: python django django-rest-framework permissions django-permissions


    【解决方案1】:

    在您的序列化程序而不是视图中验证。您可以验证validate_<field> 中的特定字段或validate 函数中的整体

    class YourModleSerializer(serializers.ModelSerializer):
        def validate_field(self, value):
            return value
            
        def validate(self, attrs):
            validated_data = super().validate(attrs)
            return validated_data
         
    

    【讨论】:

      【解决方案2】:

      创建自定义字段关系是我案例的正确方法。

      from apps.models import Daddy
      from rest_framework.serializers import PrimaryKeyRelatedField
      
      class DaddyRelatedField(PrimaryKeyRelatedField):
          default_error_messages = {
              "required": _("This field is required."),
              "does_not_exist": _("The Daddy does not exist or you don't have permission to use it."),
              "incorrect_type": _("Incorrect type. Expected pk value, received {data_type}."),
          }
      
          def get_queryset(self):
              if self.read_only:
                  return None
              queryset = Daddy.objects.all()
              return queryset.allowed_daddies(self.context["request"].user)
       
      

      然后在序列化器上

      class KiddySerializer(ModelSerializer):
          daddy = DaddyRelatedField()
      

      【讨论】:

        猜你喜欢
        • 2016-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-11
        • 2019-03-17
        • 1970-01-01
        • 1970-01-01
        • 2018-10-14
        相关资源
        最近更新 更多