使用DRF去创建标准API的时候有一个日常操作就是serializer.is_valid(),用于检查序列化对象serializer是否合规,这个方法有一个参数叫raise_exception(boolean值),用于控制是不是抛出错误啥的。在自定义的serializer内自定义validate,方法名用validate和要做特殊检查的字段用下划线连起来命名就可以直接检查,比如要实现一个需求,邻居都不能姓王,那么就需要在自己对应的serializer内新增一个叫做validata_name的方法,可以这样写
class NeighbourSerializer(serializers.Serializer):
"""
邻居们的序列化
"""
......
......
name = serializers.CharField(max_length=100, allow_null=False)
def validate_name(self, value):
if value.first_name == '王':
raise serializers.ValidationError(u'隔壁怎么可以姓王?')
这个时候当代码运行起来会发现is_valid()时会自动进行检查,假如是我,我估计是拼接字符串,validate_当标志,后面拼上field名称当方法名然后调用。但是依然很好奇框架作者大神是如何实现的,所以看了看源码,整个调用流程是从is_valid()方法开始的,is_valid()内部会调用run_validation(data),然后run_validation()(注意,这个run_validation是Serializer类的,而不是Field的,rest_fremwork下自己封装的field也有一套名称一模一样的方法,跟踪的时候容易跟错),然后在run_validation内部会调用另外一个方法,叫做to_internal_value(data),这个方法是自定义validate的关键,源码如下:
def to_internal_value(self, data):
"""
Dict of native values <- Dict of primitive datatypes.
基本数据类型 -> 本地数据类型(我理解就是Serializer自己认的数据类型),顾注释思义!这玩意儿是用来使数据类型啥的变成合 乎自己逻辑的。
"""
if not isinstance(data, Mapping):
message = self.error_messages['invalid'].format(
datatype=type(data).__name__
)
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='invalid')
ret = OrderedDict()
errors = OrderedDict()
fields = self._writable_fields
for field in fields:
# 这里的fields内容就是我们在自定义的Serializer子类内标明的字段
validate_method = getattr(self, 'validate_' + field.field_name, None)
# 这一行果不其然用字符串拼接方法名的方式去获取对应的validate方法
primitive_value = field.get_value(data)
try:
validated_value = field.run_validation(primitive_value)
# 这一行就是上面说道的field也有一套validate的方法用于校验字段
if validate_method is not None:
validated_value = validate_method(validated_value)
# 此处对自定义的validate方法进行了调用
except ValidationError as exc:
errors[field.field_name] = exc.detail
except DjangoValidationError as exc:
errors[field.field_name] = get_error_detail(exc)
except SkipField:
pass
else:
set_value(ret, field.source_attrs, validated_value)
if errors:
raise ValidationError(errors)
return ret
结论:
1.自定义validate底层就是使用的字符串拼接,所以在使用这个机制的时候尽量复制粘贴field的名称避免单词拼写问题。
2.方法内读取的fields是直接self内去获取的Serializer自身定义过的field而不是Model内的,所以要使用这一机制,务必在自己的Serializer内创建对应的field.