Django REST Framework中的序列化程序验证顺序

nma*_*rko 26 python django validation django-rest-framework

情况

在Django REST框架中使用验证时ModelSerializer,我注意到这些Meta.model字段总是经过验证,即使这样做不一定有意义.以下示例为User模型的序列化:

  1. 我有一个创建用户的端点.因此,有一个password领域和一个confirm_password领域.如果两个字段不匹配,则无法创建用户.同样,如果请求username已存在,则无法创建用户.
  2. 用户为上述每个字段POST不正确的值
  3. 的实现validate在串行的情况下(见下文),捕不匹配passwordconfirm_password领域

执行validate:

def validate(self, data):
    if data['password'] != data.pop('confirm_password'):
        raise serializers.ValidationError("Passwords do not match")
    return data
Run Code Online (Sandbox Code Playgroud)

问题

即使ValidationError被引发validate,ModelSerializer仍然会查询数据库以检查它username是否已被使用.这在从端点返回的错误列表中很明显; 存在模型和非字段错误.

因此,我想知道如何在非字段验证完成之前阻止模型验证,从而节省了对数据库的调用.

尝试解决方案

我一直试图通过DRF的来源找出这种情况发生的地方,但是我找不到我需要覆盖的内容以使其工作失败.

Kev*_*own 66

由于您的username字段很可能已unique=True设置,因此Django REST Framework会自动添加一个验证程序,该验证程序会检查以确保新用户名是唯一的.您可以通过执行实际确认repr(serializer()),这将显示所有自动生成的字段,其中包括验证器.

验证以特定的未记录顺序运行

  1. 字段反序列化称为(serializer.to_internal_valuefield.run_validators)
  2. serializer.validate_[field] 为每个字段调用
  3. 调用串行器级验证器(serializer.run_validation后跟serializer.run_validators)
  4. serializer.validate 叫做

因此,您看到的问题是在序列化程序级别验证之前调用字段级验证.虽然我不推荐它,但你可以通过extra_kwargs在serilalizer的meta中设置来删除字段级验证器.

class Meta:
    extra_kwargs = {
        "username": {
            "validators": [],
        },
    }
Run Code Online (Sandbox Code Playgroud)

您需要unique在自己的验证中重新执行检查,以及自动生成的任何其他验证器.

  • 这不再起作用了.我最后继承了字段类型,并将自定义验证放在`to_internal_value`中.这是唯一的方法.有人需要在ModelSerializers中实现自定义验证...... (8认同)

小智 20

我还试图了解在序列化程序验证期间控制如何流动,在仔细阅读 djangorestframework-3.10.3 的源代码后,我想出了以下请求流程图。我已经尽我所能地描述了流程和流程中发生的事情,但没有详细说明,因为它可以从源代码中查找。

忽略不完整的方法签名。只关注在哪些类上调用了哪些方法。

DRF_Validation_Control_Flow

假设当您调用以下代码时is_valid,您的序列化程序类 ( MySerializer(serializers.Serializer))上有一个被覆盖的方法my_serializer.is_valid()

  1. MySerializer.is_valid() 被执行。
  2. 假设您正在调用超类 ( BaseSerializer)is_valid方法(例如:super(MySerializer, self).is_valid(raise_exception)在您的MySerializer.is_valid()方法中,它将被调用。
  3. 现在因为MySerializer正在扩展serializers.Serializer,所以调用了run_validation()方法 from serializer.Serializers。这仅验证第一个数据字典。所以我们还没有开始字段级验证。
  4. 然后调用validate_empty_valuesfrom fields.Field。这再次发生在整个data领域而不是单个领域。
  5. 然后Serializer.to_internal_method被调用。
  6. 现在我们遍历序列化器上定义的每个字段。对于每个字段,首先我们调用field.run_validation()方法。如果该字段已覆盖该Field.run_validation()方法,则将首先调用该方法。在 a 的情况下,CharField它被覆盖并调用基类的run_validation方法Field。图中步骤6-2。
  7. 在那个领域,我们再次称 Field.validate_empty_values()
  8. to_internal_value字段的类型被称为未来。
  9. 现在有一个对该Field.run_validators()方法的调用。我认为这是我们通过指定validators = []字段选项在字段上添加的其他验证器一一执行的地方
  10. 完成所有这些后,我们回到Serializer.to_internal_value()方法。现在请记住,我们正在为 for 循环中的每个字段执行上述操作。现在,您在序列化程序中编写的自定义字段验证器(如 方法validate_field_name)已运行。如果在前面的任何步骤中发生异常,您的自定义验证器将不会运行。
  11. read_only_defaults()
  12. 使用我认为的默认值更新验证数据
  13. 运行对象级验证器。我认为validate()您的对象上的方法在这里运行。

  • 尊重您为使答案清晰明了而付出的努力! (6认同)