pydantic做参数校验
定义一个统一的schema类对提交的业务参数进行格式和数据约束非常有必要,
下面使用 pydantic 来封装此工具;
import logging
from pydantic import BaseModel, ValidationError, root_validator
class PydanticValidationError(Exception):
def __init__(self, msg):
self.message = msg
class BaseSchema(BaseModel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
class Config:
anystr_strip_whitespace = True
use_enum_values = True
arbitrary_types_allowed = True
@root_validator(pre=True)
def _pre_empty_data(cls, values: dict):
"""将空字符串或null字符串转换为None"""
for k, v in values.items():
if v == "" or v == "null":
values[k] = None
return values
@classmethod
def data_validation(cls, data):
"""参数校验,并自定义返回信息"""
# TODO 这里并不一定是全部的,后面如果碰到其他的,再添加
try:
res = cls.parse_obj(data)
errs = []
except ValidationError as e:
res = None
errs = e.errors()
for err in errs:
logging.exception(err)
fields = list(err["loc"])
field = fields[0]
field_info = cls.__fields__.get(field).field_info # type: ignore
type_info = err["type"].split(".")
fields[0] = field_info.title if field_info.title else field
title = ">>".join([str(tmp) for tmp in fields])
if len(type_info) == 2:
if type_info[0] == "type_error" and type_info[1] == "enum":
raise PydanticValidationError(f"{title}数据错误,请传指定可选值")
if type_info[0] == "type_error":
raise PydanticValidationError(f"{title}数据类型错误,需要是{type_info[1]}类型")
if type_info[0] == "value_error" and type_info[1] == "missing":
raise PydanticValidationError(f"{title}不能为空")
if type_info[0] == "value_error" and type_info[1] == "const":
raise PydanticValidationError(f"{title}数据错误,请传指定值")
if type_info[0] == "value_error" and type_info[1] == "ipv4address":
raise PydanticValidationError(f"{title}数据错误,需要是ipv4地址")
if len(type_info) == 3:
if type_info[2] == "not_gt":
raise PydanticValidationError(f"{title}的数值必须大于{field_info.gt}")
if type_info[2] == "not_lt":
raise PydanticValidationError(f"{title}的数值必须小于{field_info.lt}")
if type_info[2] == "not_ge":
raise PydanticValidationError(f"{title}的数值必须大于或等于{field_info.ge}")
if type_info[2] == "not_le":
raise PydanticValidationError(f"{title}的数值必须小于或等于{field_info.le}")
if type_info[2] == "min_length":
raise PydanticValidationError(f"{title}的最小字符长度为{field_info.min_length}")
if type_info[2] == "max_length":
raise PydanticValidationError(f"{title}的最大字符长度为{field_info.max_length}")
if type_info[2] == "min_items":
raise PydanticValidationError(f"{title}中的最少需要{field_info.min_items}个元素")
if type_info[2] == "max_items":
raise PydanticValidationError(f"{title}中的最多只能{field_info.min_items}个元素")
if type_info[1] == "none" and type_info[2] == "not_allowed":
raise PydanticValidationError(f"{title}不能为空")
raise PydanticValidationError(f"{title}参数错误")
return res
定义一个schema来接收参数, 它继承BaseSchema
class VictimOrgSchema(BaseSchema):
title: str = Field(max_length=50, title="组织名称", description="组织名称")
street: Optional[str] = Field(max_length=255, title="街道具体地址", description="街道具体地址", default="")
latitude: str = Field(description="经度")
longitude: str = Field(description="纬度")
first_id: Optional[int] = Field(description="一级总指挥id")
second_id: Optional[int] = Field(description="二级总指挥id")
third_id: Optional[int] = Field(description="三级总指挥id")
department_id: Optional[int] = Field(title="行业性质",description="行业性质")
org_nature: int = Field(title="组织性质",description="组织性质")
contacts: Optional[str] = Field(max_length=255, title="联系人", description="联系人")
phone: Optional[str] = Field(max_length=11, title="联系电话", description="联系电话")
logo: Union[FileStorage, str, None] = Field(description="logo")
province: int = Field(description="省份code")
city: int = Field(description="城市code")
team_target: int = Field(description="是否靶标单位", default=0)
view里面进行参数校验
args = dict(request.form) if request.form else {}
data_obj = VictimOrgSchema.data_validation(args)