掌握FastAPI与Pydantic的跨字段验证技巧


from pydantic import BaseModel, validator
class UserCreate(BaseModel):
password: str
password_confirm: str
@validator('password_confirm')
def passwords_match(cls, v, values):
if 'password' in values and v != values['password']:
raise ValueError('密码不一致')
return v
from pydantic import BaseModel, validator, root_validator
class UserCreate(BaseModel):
email: str
email_confirm: str
password: str
password_confirm: str
@validator('email_confirm')
def emails_match(cls, v, values):
if 'email' in values and v != values['email']:
raise ValueError('邮箱地址不匹配')
return v
@root_validator
def check_passwords(cls, values):
pw = values.get('password')
pw_confirm = values.get('password_confirm')
if pw and pw_confirm and pw != pw_confirm:
raise ValueError('两次输入的密码不一致')
return values
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, validator
app = FastAPI()
class RegistrationForm(BaseModel):
username: str
email: str
email_confirm: str
password: str
password_confirm: str
@validator('email_confirm')
def emails_match(cls, v, values):
if values.get('email') != v:
raise ValueError('邮箱确认不匹配')
return v
@validator('password_confirm')
def passwords_match(cls, v, values):
if values.get('password') != v:
raise ValueError('密码确认不匹配')
return v
@app.post("/register")
async def user_register(form: RegistrationForm):
# 实际业务处理(此处仅为示例)
return {
"message": "注册成功",
"username": form.username,
"email": form.email
}
{
"username": "fastapi_user",
"email": "user@example.com",
"email_confirm": "user@example.com",
"password": "secure123",
"password_confirm": "secure123"
}
{
"email": "user@example.com",
"email_confirm": "user@gmail.com",
"password": "123",
"password_confirm": "1234"
}
{
"detail": [
{
"loc": ["body", "username"],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": ["body", "email_confirm"],
"msg": "邮箱确认不匹配",
"type": "value_error"
},
{
"loc": ["body", "password_confirm"],
"msg": "密码确认不匹配",
"type": "value_error"
}
]
}
from pydantic import BaseModel, validator
import re
class EnhancedValidator(BaseModel):
@classmethod
def validate_email_format(cls, v):
pattern = r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$"
if not re.match(pattern, v):
raise ValueError('无效的邮箱格式')
return v
class UserModel(EnhancedValidator):
email: str
email_confirm: str
@validator('email')
def valid_email(cls, v):
return cls.validate_email_format(v)
@validator('email_confirm')
def confirm_email(cls, v, values):
cls.validate_email_format(v)
if v != values.get('email'):
raise ValueError('邮箱地址不匹配')
return v
from pydantic import BaseModel, root_validator
from datetime import datetime
class EventForm(BaseModel):
start_time: datetime
end_time: datetime
@root_validator
def time_validation(cls, values):
start = values.get('start_time')
end = values.get('end_time')
if start and end:
if start >= end:
raise ValueError('开始时间必须早于结束时间')
if (end - start).days > 7:
raise ValueError('事件持续时间不能超过7天')
return values
点击查看答案 正确答案:B) @root_validator 解析:root_validator可以在所有字段验证完成后访问全部字段值,适合处理多个字段的联合验证逻辑。当验证逻辑涉及三个及以上字段,或需要综合判断多个字段关系时,使用root_validator更为合适。
点击查看答案 正确答案:C) 根据字段定义顺序 解析:Pydantic默认按照模型字段的定义顺序执行验证。如果需要改变验证顺序,可以使用@validator的pre=True参数将该验证器设置为预处理阶段。
{
"detail": [
{
"loc": ["body", "password_confirm"],
"msg": "密码不一致",
"type": "value_error"
}
]
}
from fastapi import HTTPException
from pydantic import ValidationError
@app.post("/register")
async def register_user(data: dict):
try:
form = RegistrationForm(**data)
except ValidationError as e:
raise HTTPException(400, detail=e.errors())
{
"detail": [
{
"loc": ["body", "email"],
"msg": "field required",
"type": "value_error.missing"
}
]
}
from typing import Optional
class UserModel(BaseModel):
email: Optional[str] = None
# 优化后的密码验证器示例
@validator('password')
def validate_password(cls, v):
if len(v) < 8:
raise ValueError('密码至少8个字符')
if not any(c.isupper() for c in v):
raise ValueError('必须包含大写字母')
if not any(c.isdigit() for c in v):
raise ValueError('必须包含数字')
return v
评论
发表评论