Python表单校验案例如何校验表单数据

wen python案例 21

本文目录导读:

Python表单校验案例如何校验表单数据

  1. 基础表单校验(不使用框架)
  2. 使用Pydantic进行表单校验(推荐)
  3. 使用WTForms(Flask常用)
  4. 综合校验案例
  5. Web框架集成示例(FastAPI)
  6. 建议

我来为你提供几个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)

建议

  1. 生产环境推荐使用Pydantic或WTForms,它们提供了完善的校验机制和错误处理
  2. 自定义校验逻辑应该分离到独立的校验函数中
  3. 注意安全性:不要在前端完全信赖前端的校验,后端必须重新校验
  4. 提供友好的错误信息:错误提示应该清晰、具体
  5. 考虑国际化:如果需要支持多语言,错误信息应该支持翻译

选择哪种方式取决于你的项目需求:

  • 简单项目:可以使用基础校验
  • Flask项目:推荐WTForms
  • FastAPI项目:推荐Pydantic
  • 复杂的校验逻辑:可以综合使用多种方法

抱歉,评论功能暂时关闭!