本文目录导读:

我来为你提供几个Python表单校验的完整案例,涵盖不同场景和校验方法。
基础表单校验(不使用框架)
import re
from typing import Dict, List, Optional
class FormValidator:
"""基础表单校验器"""
@staticmethod
def validate_required(value: str, field_name: str) -> Optional[str]:
"""校验必填字段"""
if not value or not value.strip():
return f"{field_name} 是必填项"
return None
@staticmethod
def validate_email(email: str) -> Optional[str]:
"""校验邮箱格式"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(pattern, email):
return "邮箱格式不正确"
return None
@staticmethod
def validate_phone(phone: str) -> Optional[str]:
"""校验手机号(中国)"""
pattern = r'^1[3-9]\d{9}$'
if not re.match(pattern, phone):
return "手机号格式不正确"
return None
@staticmethod
def validate_length(value: str, min_length: int = None, max_length: int = None, field_name: str = "") -> Optional[str]:
"""校验长度"""
if min_length and len(value) < min_length:
return f"{field_name} 长度不能少于{min_length}个字符"
if max_length and len(value) > max_length:
return f"{field_name} 长度不能超过{max_length}个字符"
return None
@staticmethod
def validate_password(password: str) -> List[str]:
"""校验密码强度"""
errors = []
if len(password) < 8:
errors.append("密码长度不能少于8位")
if not re.search(r'[A-Z]', password):
errors.append("密码需要包含大写字母")
if not re.search(r'[a-z]', password):
errors.append("密码需要包含小写字母")
if not re.search(r'\d', password):
errors.append("密码需要包含数字")
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', password):
errors.append("密码需要包含特殊字符")
return errors
# 使用示例
def validate_registration_form(data: Dict) -> Dict:
"""校验注册表单"""
errors = {}
validator = FormValidator()
# 校验用户名
username_error = validator.validate_required(data.get('username', ''), '用户名')
if not username_error:
username_error = validator.validate_length(data.get('username', ''), 3, 20, '用户名')
if username_error:
errors['username'] = username_error
# 校验邮箱
email_error = validator.validate_required(data.get('email', ''), '邮箱')
if not email_error:
email_error = validator.validate_email(data.get('email', ''))
if email_error:
errors['email'] = email_error
# 校验手机号
phone_error = validator.validate_phone(data.get('phone', ''))
if phone_error:
errors['phone'] = phone_error
# 校验密码
password_errors = validator.validate_password(data.get('password', ''))
if password_errors:
errors['password'] = password_errors
return errors
# 测试
test_data = {
'username': 'ab',
'email': 'invalid-email',
'phone': '12345678901',
'password': 'weak'
}
errors = validate_registration_form(test_data)
print("表单校验结果:")
for field, error in errors.items():
if isinstance(error, list):
print(f"{field}: {', '.join(error)}")
else:
print(f"{field}: {error}")
使用Pydantic进行表单校验(推荐)
from pydantic import BaseModel, EmailStr, Field, validator, field_validator
from typing import Optional
from datetime import date, datetime
class UserRegistrationForm(BaseModel):
"""用户注册表单 - Pydantic版本"""
username: str = Field(..., min_length=3, max_length=20, description="用户名")
email: EmailStr = Field(..., description="邮箱")
password: str = Field(..., min_length=8, description="密码")
confirm_password: str = Field(..., description="确认密码")
age: int = Field(ge=0, le=150, default=18, description="年龄")
phone: Optional[str] = Field(None, description="手机号")
birthday: Optional[date] = Field(None, description="生日")
@field_validator('password')
@classmethod
def validate_password_strength(cls, v):
"""自定义密码强度校验"""
import re
if not re.search(r'[A-Z]', v):
raise ValueError('密码必须包含大写字母')
if not re.search(r'[a-z]', v):
raise ValueError('密码必须包含小写字母')
if not re.search(r'\d', v):
raise ValueError('密码必须包含数字')
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', v):
raise ValueError('密码必须包含特殊字符')
return v
@field_validator('confirm_password')
@classmethod
def passwords_match(cls, v, info):
"""校验两次密码一致"""
if 'password' in info.data and v != info.data['password']:
raise ValueError('两次密码不一致')
return v
@field_validator('phone')
@classmethod
def validate_phone(cls, v):
"""校验手机号"""
if v is not None:
import re
if not re.match(r'^1[3-9]\d{9}$', v):
raise ValueError('手机号格式不正确')
return v
@field_validator('username')
@classmethod
def validate_username(cls, v):
"""校验用户名格式"""
import re
if not re.match(r'^[a-zA-Z0-9_]+$', v):
raise ValueError('用户名只能包含字母、数字和下划线')
return v
# 使用示例
try:
# 正确的数据
valid_data = UserRegistrationForm(
username="john_doe",
email="john@example.com",
password="StrongP@ss1",
confirm_password="StrongP@ss1",
age=25,
phone="13812345678",
birthday=date(1998, 1, 1)
)
print("校验通过!")
print(f"用户名: {valid_data.username}")
except Exception as e:
print(f"校验失败: {e}")
# 错误的数据
try:
invalid_data = UserRegistrationForm(
username="ab", # 太短
email="invalid-email", # 邮箱格式错误
password="weak", # 密码太弱
confirm_password="different", # 密码不一致
age=200 # 年龄太大
)
except Exception as e:
print(f"\n校验失败: {e}")
使用WTForms(Flask常用)
from wtforms import Form, StringField, IntegerField, PasswordField, validators
from wtforms.validators import DataRequired, Email, Length, NumberRange, EqualTo, Regexp
class RegistrationForm(Form):
"""注册表单 - WTForms版本"""
username = StringField('用户名', [
DataRequired(message='用户名不能为空'),
Length(min=3, max=20, message='用户名长度需要在3-20个字符之间'),
Regexp(r'^[a-zA-Z0-9_]+$', message='用户名只能包含字母、数字和下划线')
])
email = StringField('邮箱', [
DataRequired(message='邮箱不能为空'),
Email(message='邮箱格式不正确')
])
password = PasswordField('密码', [
DataRequired(message='密码不能为空'),
Length(min=8, message='密码长度不能少于8个字符')
])
confirm_password = PasswordField('确认密码', [
DataRequired(message='请确认密码'),
EqualTo('password', message='两次密码不一致')
])
age = IntegerField('年龄', [
NumberRange(min=0, max=150, message='年龄必须在0-150之间')
])
def validate_password(self, field):
"""自定义密码校验规则"""
import re
if not re.search(r'[A-Z]', field.data):
raise validators.ValidationError('密码必须包含大写字母')
if not re.search(r'[a-z]', field.data):
raise validators.ValidationError('密码必须包含小写字母')
if not re.search(r'\d', field.data):
raise validators.ValidationError('密码必须包含数字')
if not re.search(r'[!@#$%^&*(),.?":{}|<>]', field.data):
raise validators.ValidationError('密码必须包含特殊字符')
# 使用示例
def validate_wtforms_form(form_data: dict) -> dict:
"""校验WTForms表单"""
form = RegistrationForm(data=form_data)
if form.validate():
return {'valid': True, 'errors': {}}
else:
errors = {}
for field, field_errors in form.errors.items():
errors[field] = field_errors
return {'valid': False, 'errors': errors}
# 测试
test_data = {
'username': 'test_user',
'email': 'test@example.com',
'password': 'StrongP@ss1',
'confirm_password': 'StrongP@ss1',
'age': 25
}
result = validate_wtforms_form(test_data)
print(f"校验结果: {'通过' if result['valid'] else '失败'}")
if not result['valid']:
print(f"错误信息: {result['errors']}")
综合校验案例
from dataclasses import dataclass
from typing import Dict, Optional, List
import re
from datetime import datetime
@dataclass
class ValidationRule:
"""校验规则定义"""
required: bool = False
min_length: Optional[int] = None
max_length: Optional[int] = None
regex: Optional[str] = None
email: bool = False
phone: bool = False
custom_validator: Optional[callable] = None
class AdvancedFormValidator:
"""高级表单校验器"""
def __init__(self, rules: Dict[str, ValidationRule]):
self.rules = rules
def validate(self, data: Dict) -> Dict:
"""执行校验"""
errors = {}
for field_name, rule in self.rules.items():
value = data.get(field_name, '')
# 检查必填
if rule.required and (value is None or str(value).strip() == ''):
errors[field_name] = f'{field_name} 是必填项'
continue
# 如果值为空且非必填,跳过其他校验
if value is None or str(value).strip() == '':
continue
# 转换为字符串
str_value = str(value)
# 校验长度
if rule.min_length and len(str_value) < rule.min_length:
errors[field_name] = f'{field_name} 长度不能少于{rule.min_length}个字符'
continue
if rule.max_length and len(str_value) > rule.max_length:
errors[field_name] = f'{field_name} 长度不能超过{rule.max_length}个字符'
continue
# 校验正则
if rule.regex and not re.match(rule.regex, str_value):
errors[field_name] = f'{field_name} 格式不正确'
continue
# 校验邮箱
if rule.email:
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if not re.match(email_pattern, str_value):
errors[field_name] = '邮箱格式不正确'
continue
# 校验手机号
if rule.phone:
phone_pattern = r'^1[3-9]\d{9}$'
if not re.match(phone_pattern, str_value):
errors[field_name] = '手机号格式不正确'
continue
# 自定义校验器
if rule.custom_validator:
try:
error_msg = rule.custom_validator(str_value)
if error_msg:
errors[field_name] = error_msg
except Exception as e:
errors[field_name] = str(e)
return errors
# 使用示例
def custom_password_validator(password: str) -> Optional[str]:
"""自定义密码校验"""
if not re.search(r'[A-Z]', password):
return '密码必须包含大写字母'
if not re.search(r'[a-z]', password):
return '密码必须包含小写字母'
if not re.search(r'\d', password):
return '密码必须包含数字'
return None
# 定义校验规则
registration_rules = {
'username': ValidationRule(
required=True,
min_length=3,
max_length=20,
regex=r'^[a-zA-Z0-9_]+$'
),
'email': ValidationRule(
required=True,
email=True
),
'phone': ValidationRule(
phone=True
),
'password': ValidationRule(
required=True,
min_length=8,
custom_validator=custom_password_validator
),
'bio': ValidationRule(
max_length=500
)
}
# 创建校验器
validator = AdvancedFormValidator(registration_rules)
# 测试数据
test_data = {
'username': 'test_user',
'email': 'test@example.com',
'phone': '13812345678',
'password': 'WeakPass1',
'bio': 'Hello, this is my bio!'
}
# 执行校验
errors = validator.validate(test_data)
if not errors:
print("所有字段校验通过!")
else:
print("校验失败:")
for field, error in errors.items():
print(f" {field}: {error}")
Web框架集成示例(FastAPI)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from typing import Optional
app = FastAPI()
class UserCreate(BaseModel):
"""创建用户请求模型"""
username: str = Field(..., min_length=3, max_length=50)
email: str = Field(...)
password: str = Field(..., min_length=8)
full_name: Optional[str] = None
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, v):
"""自定义校验"""
if isinstance(v, dict):
# 校验邮箱格式
import re
email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
if 'email' in v and not re.match(email_pattern, v['email']):
raise ValueError('邮箱格式不正确')
# 校验密码强度
if 'password' in v:
if not re.search(r'[A-Z]', v['password']):
raise ValueError('密码必须包含大写字母')
if not re.search(r'[a-z]', v['password']):
raise ValueError('密码必须包含小写字母')
if not re.search(r'\d', v['password']):
raise ValueError('密码必须包含数字')
return v
@app.post("/users/")
async def create_user(user: UserCreate):
"""创建用户接口"""
# 如果校验失败,FastAPI会自动返回422状态码和错误信息
return {
"message": "用户创建成功",
"username": user.username,
"email": user.email
}
# 模拟请求
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
建议
- 生产环境推荐使用Pydantic或WTForms,它们提供了完善的校验机制和错误处理
- 自定义校验逻辑应该分离到独立的校验函数中
- 注意安全性:不要在前端完全信赖前端的校验,后端必须重新校验
- 提供友好的错误信息:错误提示应该清晰、具体
- 考虑国际化:如果需要支持多语言,错误信息应该支持翻译
选择哪种方式取决于你的项目需求:
- 简单项目:可以使用基础校验
- Flask项目:推荐WTForms
- FastAPI项目:推荐Pydantic
- 复杂的校验逻辑:可以综合使用多种方法