【问题标题】:How to raise an error / return a {"foo":["This field is required."]} response in Django REST如何在 Django REST 中引发错误/返回 {"foo":["This field is required."]} 响应
【发布时间】:2015-09-04 15:26:07
【问题描述】:

假设我有一个视图,我需要在调用 serializer.save 之前检查是否提供了一个字段,以确保我没有收到字典键错误:

class BarView(CreateAPIView):
    serializer_class = BarSerializer
    queryset = Bar.objects.all()
    def perform_create(self, serializer):
        if 'foo' not in self.request.data:
            raise ParseError('foo field required.')
        foo = get_object_or_404(Foo, pk=self.request.data['foo'])
        if foo.counter == 10:
            raise ParseError('foo limit reached.')
        return serializer.save(user=self.request.user, foo=foo)

我不想返回"foo field required.",而是想返回一条与 Django REST 返回的消息相同的消息,例如{"foo":["This field is required."]}

有没有更好的方法来做到这一点?或许用序列化器单独验证 foo 字段?

更新:我忘了说用户字段也是必需的。

Bar 的模型是:

class Bar(models.Model):
    user = models.ForeignKey(User, db_index=True, editable=False)
    foo = models.ForeignKey(Foo, db_index=True)

【问题讨论】:

  • 您的serializer 是否返回is_valid()True
  • foo 字段是您模型的一部分吗?
  • 是的,我刚刚将模型添加到原始问题中。

标签: python django django-rest-framework


【解决方案1】:

是的,

只需查看文档:Validation

我假设字段fooBar 模型的一部分,如果不是,请将其添加到Meta 中的fields):

向 BarSerializer 添加验证:

class BarSerializer(serializers.ModelSerializer):
    def validate_foo(self, value):
        if not value:
            raise serializers.ValidationError("foo field required.")
        if Foo.objects.filter(pk=value, counter__gte=10).exists():
            raise serializers.ValidationError("foo limit reached.")
        return value

    class Meta:
        model = Bar

然后通过扩展来创建你的视图:

from rest_framework.exceptions import ValidationError

class MyCreateAPIView(CreateAPIView):        
    def post(self, request, *args, **kwargs):
        try:
             return super(BarView, self).post(request, *args, **kwargs)
        except ValidationError as e:
             return Response(e.detail, , status=status.HTTP_400_BAD_REQUEST)

    def create(self,request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            self.perform_create(serializer)       
        except DjangoValidationError as e:
            raise ValidationError(e.messages)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(serializer):
        # do your stuff
        serializer.save()

【讨论】:

  • 如何在帖子中保存请求用户(这也是必填字段)?
  • @tyebillion 改进了答案以满足您的需求,只需使用此类而不是 CreateAPIView
【解决方案2】:

是的,最好的方法是使用Meta 类中的extra_kwargs 选项在序列化程序中设置foo 字段。

DRF 将自动为您处理验证。您无需自己提出此验证错误。

class Meta:
    ...
    extra_kwargs = {'foo': {'required':True}} # make 'foo' a required field.

现在,每当request 中没有传递foo 字段时,serializer.errors 字典中就会有一个键foo,其值将是This field is required.

另外,创建一个validate_foo() 函数来验证foo_object.counter 的限制。

def validate_foo(self, value):
    self.foo_object = get_object_or_404(Foo, pk=value) # get the 'foo' object
    if self.foo_object.counter == 10: # check for limits
        raise serializers.ValidationError('foo limit reached.') # raise error
    return value # must return the value at the end

最终代码:

serializers.py

class BarSerializer(serializers.ModelSerializer):

    class Meta:
        ...
        extra_kwargs = {'foo': {'required':True}} # make 'foo' a required field.

    def validate_foo(self, value):
        self.foo_object = get_object_or_404(Foo, pk=value)
        if self.foo_object.counter == 10: 
            raise serializers.ValidationError('foo limit reached.') 
        return value

views.py

在您看来,您需要覆盖perform_create() 并将userserializer.foo_object 传递给serializer.save() 函数。

class BarView(CreateAPIView):
    serializer_class = BarSerializer
    queryset = Bar.objects.all()

    def perform_create(self, serializer):
        return serializer.save(user=self.request.user, foo=serializer.foo_object)

【讨论】:

  • 我得到一个错误:异常类型:AttributeError,异常值:'BarSerializer'对象没有属性'foo_object',在最后一行“return serializer...”
  • 您是否在序列化程序中添加了validate_foo() 函数?在该函数中,我们将foo_object 设置为序列化程序的属性,然后在调用serializer.save() 时使用此值。
  • 是的。我的序列化程序的 Meta 部分也有 read_only_fields = ('foo',)。
  • 您收到此错误是因为将 foo 声明为只读字段。当字段为只读时,不会考虑对其进行验证,因此永远不会调用validate_foo,从而永远不会在序列化程序上设置foo_object。尝试将 foo 删除为只读字段,它应该可以正常工作。
  • 我仍然遇到同样的错误:“BarSerializer”对象没有属性“foo_object”。验证不应该在模型中进行吗?
【解决方案3】:

您可以使用用户定义的消息返回Response

if 'foo' not in self.request.data:
    return Response({"foo":["This field is required."]})

【讨论】:

  • 不幸的是,在 perform_create 中返回 Response 不起作用。我没有引发异常,而是得到异常类型:KeyError,异常值:'foo'。
猜你喜欢
  • 1970-01-01
  • 2021-02-28
  • 1970-01-01
  • 1970-01-01
  • 2018-11-07
  • 2022-11-07
  • 1970-01-01
  • 2021-03-27
  • 1970-01-01
相关资源
最近更新 更多