根据官方文档 django validators 所属的内容,我们可以给字段添加专属的验证器。
官方验证器
官方文档中提供了各种常用的验证器,比如
- validate_ipv4_address
- URLValidator
- validate_slug 字母、数字、下划线或连字符组成
- EmailValidator
具体参数用法可以参考文档,一般是提供错误时输出内容、错误码等参数
自定义验证器
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
参考资料
公众号
扫码订阅最新深度技术文,回复【资源】获取技术大礼包
评论