Django Rest Framework: 过滤/验证相关字段

3

我有两个模型:Foo含有一个owner字段,而Bar与Foo有关联:

class Foo(models.Model):
    owner = models.ForeignKey('auth.User')  
    name = models.CharField(max_length=20, null=True)

class Bar(models.Model):
    foo = models.OneToOneField(Foo, related_name='bar')
    [...]

我使用HyperlinkedModelSerializer进行表示:

class BarSerializer(serializers.HyperlinkedModelSerializer): 
    foo = serializers.HyperlinkedRelatedField(view_name='foo-detail', queryset=Foo.objects.all())
    [...]

    class Meta:
        model = Bar
        fields = ('foo', [...])

class FooSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.SlugRelatedField(read_only=True, slug_field='username')
    bar = serializers.HyperlinkedRelatedField(view_name='bar-detail', read_only=True)

    class Meta:
        model = Foo
        fields = ('name', 'bar', 'owner')

我的看法如下:

class FooViewSet(viewsets.ModelViewSet):
    queryset = Foo.objects.all()
    serializer_class = FooSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwner,)

    def get_queryset(self):
        user = self.request.user

        if not user.is_authenticated():
            return Foo.objects.none()

        if user.username == "admin":
            return Foo.objects.all()

        return Foo.objects.filter(owner=user)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

class BarViewSet(viewsets.ModelViewSet):
    queryset = Bar.objects.all()
    serializer_class = BarSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwner,)

    def get_queryset(self):
        user = self.request.user

        if not user.is_authenticated():
            return Bar.objects.none()

        if user.username == "admin":
            return Bar.objects.all()

        return Bar.objects.filter(foo__owner=user)

我不希望用户A能够看到用户B的东西,反之亦然。这个功能基本上可以很好地实现,但有一个例外:
用户A创建了一个Foo实例,但并没有立即创建链接到Foo的Bar实例。此时,用户B可以猜测到用户A的Foo实例的URL,并在创建他自己的Bar实例时指定该URL。
此时,用户A会得到一个他没有创建的Bar实例。
我对Django和rest_framework都很陌生,所以我不知道如何解决这个问题。有人能帮我找到正确的方向吗?我的第一个想法是使用BarSerializer中的foo字段来过滤Foos,使用queryset。但我不知道如何从那里访问auth.User对象。
1个回答

2

如果您在序列化器的上下文中包含请求,则可以在Bar序列化器内访问该请求。 然后,您可以在Bar序列化器内进行字段级别验证

def validate_foo(self, val):
    user = self.context['request'].user

    try:
        foo = Foo.objects.get(pk=val)
    except Foo.DoesNotExist:
        raise serializers.ValidationError("Some Error")

    if foo.user is not user:
        raise serializers.ValidationError("Some Error")

   return value

值得注意的是,ModelViewSet最终从GenericAPIView派生而来,而GenericAPIView已经在get_serializer_context()中包含了请求。https://github.com/encode/django-rest-framework/blob/a251b9379200420062cad9e3c68fde7c0e6b3fdc/rest_framework/generics.py#L132 - wasabigeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接