django 字段验证器/DRF

小熊 2022年9月8日Python1 138 views2742字阅读9分8秒阅读模式

根据官方文档 django validators 所属的内容,我们可以给字段添加专属的验证器。

官方验证器

官方文档中提供了各种常用的验证器,比如

具体参数用法可以参考文档,一般是提供错误时输出内容、错误码等参数

自定义验证器

model

from utils.validate import validate_script_name, validate_script_type, validate_list_json

class Script(models.Model):
    name = models.CharField(max_length=100, blank=False, null=True, verbose_name='脚本名称',
                            validators=[validate_script_name])
    script_type = models.CharField(max_length=100, blank=False, null=True, verbose_name='脚本类型',
                                   validators=[validate_script_type])
    input_params = models.JSONField(null=False, blank=False, default=list, validators=[validate_list_json])
    output_params = models.JSONField(null=False, blank=False, default=list, validators=[validate_list_json])

validate.py

def validate_script_name(value):
    if not isinstance(value, str):
        raise ValidationError(_('%(value)s is not an str type'), params={'value': value}, )
    if not re.match(r'^[a-zA-Z0-9_]+\.(sh|py)', value):
        raise ValidationError(_('%(value)s is not match ' + r"^[a-zA-Z0-9_]+\.(sh|py)"), params={'value': value}, )
    else:
        if re.match(r'^[a-zA-Z0-9_]+\.(sh|py)', value).span()[1]"), params={'value': value}, )


def validate_script_type(value):
    if value not in ['python', 'shell']:
        raise ValidationError(_('%(value)s is not in "python" or "shell"'), params={'value': value}, )

def validate_list_json(value):
    if not isinstance(value, list):
        raise ValidationError(_('%(value)s is not an list type'), params={'value': value}, )

DRF中的实现方式

我们观察源码部分 serializers.py?L479

class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
    .....
    def to_internal_value(self, data):
        for field in fields:
            validate_method = getattr(self, 'validate_' + field.field_name, None)
            primitive_value = field.get_value(data)
            try:
                validated_value = field.run_validation(primitive_value)
                if validate_method is not None:
                    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)
            except SkipField:
                pass
            else:
                set_value(ret, field.source_attrs, validated_value)
        .......

其实会遍历所有的字段,并且使用传入的值进行校验包括各种基础的校验,比如类型、长度

小心失效

我们在多对多关系创建/更新数据(一对多、一对一类似)中有写到使用[1,2,3]的方式传入多端的实体id 自动获取实体进行插入,查询的时候又展开多端的数据展示。使用的是在序列化类中重写create方法、增加TagsReadOnly属性来展开内容、重写to_internal_value来屏蔽验证。

确实是可以达到目的,但是相当于屏蔽了原先所有字段的验证器。应该先删掉to_internal_value的重写如下

class ScriptSerializer(serializers.ModelSerializer):
    '''
    ScriptSerializer
    '''

    labels = LabelsReadOnly(many=True)
    ...
    def create(self, validated_data):
    ...
    def update(self, instance, validated_data):
    ...

再为readonly类,单单跳过该字段的验证方法即可

class LabelsReadOnly(serializers.ModelSerializer):
    class Meta:
        model = ScriptLabel
        fields = ['id', 'key_name', 'value_name', ]

    def to_internal_value(self, data):
        return data

    def run_validators(self, value):
        return

参考资料

参考:django validators

weinxin
公众号
在号内与我交流,回复【资源】获取技术大礼包
小熊