【问题标题】:Serializer validate function is not called DRF序列化器验证函数不称为 DRF
【发布时间】:2018-08-30 05:23:00
【问题描述】:
class ChildSerializer(serializers.ModelSerializer):


    class Meta:
         model = Child
         fields = '__all__'


class ParentSerializer(serializers.ModelSerializer):
    """
    Serializer for task
    """
    def validate_title(self, data):
        if not data.get('title'):
            raise serializers.ValidationError('Please set title')
        return data

Post 时不调用验证函数,以及如何将自定义错误提供给 ChildSerializer,

【问题讨论】:

  • validate_title() 不会调用,除非您调用 serializer.is_valid()
  • DRF 具有验证级别层次结构,字段验证基于source code 处于4 或5 级别。如果 DRF 验证首先遇到错误(即空白、空字段验证...),您的字段验证可能不会被命中。没有覆盖!

标签: python django api django-rest-framework


【解决方案1】:

我遇到了一个类似的问题,我的自定义验证字段没有被调用。我写它是为了绕过不正确的 DRF 验证(更多细节如下所示,但不是答案所必需的)。

查看 DRF 源代码,我发现了我的问题:DRF 总是先使用其代码验证您的字段,然后再使用您的自定义代码进行验证。

''' rest-framework/serializers.py '''

for field in fields:
    validate_method = getattr(self, 'validate_' + field.field_name, None)
    primitive_value = field.get_value(data)
    try:
        # DRF validation always runs first!
        # If DRF validator throws, then custom validation is not called
        validated_value = field.run_validation(primitive_value)
        if validate_method is not None:
            # this is your custom validation
            validated_value = validate_method(validated_value)
    except ValidationError as exc:
        errors[field.field_name] = exc.detail
    except DjangoValidationError as exc:
        errors[field.field_name] = get_error_detail(exc)

回答:自定义验证器不能用于绕过 DRF 的验证器,因为它们始终会先运行,并且会在您说它有效之前引发异常。

(对于那些感兴趣的人,我遇到的验证错误是这样的:ModelSerializer used for ModelA,它与 ModelB 具有 OneToOne 关系。ModelB 的 pk 有一个 UUID。DRF 在验证时抛出错误'53abb068-0286-411e-8729-0174635c5d81' is not a valid UUID.,即不正确,真的很气人。)

【讨论】:

    【解决方案2】:

    您的 ParentSerializer 验证方法存在一些问题。假设您的 ParentSerializer 模型中有一个 title 字段。对于字段级验证,您将获得字段而不是整个数据。也就是说validate_title 函数应该有title(数据的标题字段)作为参数而不是data。所以你不必检查data.get('title') 是否存在titleReference

    class ParentSerializer(serializers.ModelSerializer):
        """
        Serializer for task
        """
        def validate_title(self, title):
            if not title:
                raise serializers.ValidationError('Please set title')
            return title
    

    【讨论】:

      【解决方案3】:

      除了@sean.hudson 的回答之外,我还试图弄清楚如何覆盖子序列化程序验证。

      通过覆盖 ParentSerialzer 的 to_internal_value,可以“跳过”或更准确地忽略子序列化程序验证错误:

      class ParentSerializer(serializers.ModelSerializer):
          children = ChildSerializer(many=True)
      
          def to_internal_value(self, *args, **kwargs):
              try:
                  # runs the child serializers
                  return super().to_internal_value(*args, **kwargs)
              except ValidationError as e:
                  # fails, and then overrides the child errors with the parent error
                  return self.validate(self.initial_data)
      
          def validate(self, attrs):
              errors = {}
              errors['custom_override_error'] = 'this ignores and overrides the children serializer errors'
              if len(errors):
                  raise ValidationError(errors)
              return attrs
          class Meta:
              model = Parent
      

      【讨论】:

        【解决方案4】:

        我的问题是我有自己的自定义 to_internal_value 方法。删除它可以解决问题。

        class EventSerializer(serializers.Serializer):
            end_date = serializers.DateTimeField(format=DATE_FORMAT, required=True)
            start_date = serializers.DateTimeField(format=DATE_FORMAT, required=True)
            description = serializers.CharField(required=True)
        
        
            def validate_start_date(self, start_date):
                return start_date
        
            def validate_end_date(self, end_date):
                return end_date
        
            # def to_internal_value(self, data):
            #     if data.get('start_date', False):
            #         data['start_date'] = datetime.strptime(data['start_date'], DATE_FORMAT)
            #     if data.get('end_date', False):
            #         data['end_date'] = datetime.strptime(data['end_date'], DATE_FORMAT)
            #     return data
        

        【讨论】:

          【解决方案5】:

          想补充一下官方文档说的,希望对你有帮助。

          字段级验证

          您可以通过将 .validate_ 方法添加到您的 Serializer 子类来指定自定义字段级验证。这些类似于 Django 表单上的 .clean_ 方法。

          这些方法采用单个参数,即需要验证的字段值。

          您的 validate_ 方法应返回验证值或引发 serializers.ValidationError。例如:

          from rest_framework import serializers
          
          class BlogPostSerializer(serializers.Serializer):
              title = serializers.CharField(max_length=100)
              content = serializers.CharField()
          
          def validate_title(self, value):
              """
              Check that the blog post is about Django.
              """
              if 'django' not in value.lower():
                  raise serializers.ValidationError("Blog post is not about Django")
              return value`
          

          【讨论】:

          • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-08-14
          • 1970-01-01
          • 1970-01-01
          • 2020-05-23
          • 1970-01-01
          • 2017-11-09
          • 1970-01-01
          相关资源
          最近更新 更多