Flask注册表单提交重复值时自定义校验未生效触发IntegrityError报错
问题原因
表单校验失效、直接抛出数据库重复键错误,由2个核心代码错误导致:
- forms.py中自定义字段校验方法写法错误:WTForms的
validate_<字段名>格式的自定义校验,检测到非法值时必须抛出ValidationError异常,当前代码是return异常对象,校验逻辑不会触发失败标记,表单会判定为校验通过,直接执行后续数据库插入操作,最终触发数据库的唯一约束报错。 - 前端模板错误渲染变量写错:所有字段错误遍历循环中,定义的循环变量是
error,但渲染时调用的是message变量,就算校验生成了错误信息也无法在页面显示。 - 额外隐患:User模型的email字段未设置
unique=True唯一约束,后续存在重复邮箱入库报错的风险。
修复方案
1. 修改forms.py自定义校验逻辑
将两个校验方法中返回异常的写法改为抛出异常,修正后代码如下:
import flask_wtf import wtforms import wtforms.validators from yff.models import User class RegistrationForm(flask_wtf.FlaskForm): username = wtforms.StringField('Username', validators=[wtforms.validators.DataRequired(), wtforms.validators.Length(min=3, max=20)]) email = wtforms.StringField('Email', validators=[wtforms.validators.DataRequired(), wtforms.validators.Email()]) password = wtforms.PasswordField('Password', validators=[wtforms.validators.DataRequired()]) confirmPassword = wtforms.PasswordField('Confirm Password', validators=[wtforms.validators.DataRequired(), wtforms.validators.EqualTo('password')]) remember = wtforms.BooleanField('Remember me') submit = wtforms.SubmitField('Create Account') def validate_username(self, username): user = User.query.filter_by(username=username.data).first() if user: # 改为抛出异常而非return raise wtforms.validators.ValidationError(message='This username is already taken. Please choose a different one.') def validate_email(self, email): user = User.query.filter_by(email=email.data).first() if user: raise wtforms.validators.ValidationError(message='This email is already taken. Please choose a different one.') class LoginForm(flask_wtf.FlaskForm): email = wtforms.StringField('Email', validators=[wtforms.validators.DataRequired(), wtforms.validators.Email()]) password = wtforms.PasswordField('Password', validators=[wtforms.validators.DataRequired()]) remember = wtforms.BooleanField('Remember Me') submit = wtforms.SubmitField('Login')
2. 修改前端模板错误渲染逻辑
将所有字段错误遍历块中的{{ message }}替换为{{ error }},以用户名字段为例,修正后写法:
<div id="username-field"> {{ form.username.label(class="form-label") }} {% if form.username.errors %} {% for error in form.username.errors %} <span>{{ error }}</span> {% endfor %} {% endif %} {{ form.username(class='form-el') }} </div>
邮箱、密码、确认密码三个字段的错误块做完全相同的修改即可。
3. (可选优化)补全邮箱字段唯一约束
修改models.py中email字段定义,添加唯一约束,避免后续出现重复邮箱入库报错:
from yff import db, login_manager from flask_login import UserMixin @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) class User(db.Model, UserMixin): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(20), unique=True, nullable=False) password = db.Column(db.String(120), unique=False, nullable=False) # 新增unique=True约束 email = db.Column(db.String(50), unique=True, nullable=False) profile_pic = db.Column(db.String(30), unique=False, default="default.jpg") def __repr__(self): return f'{self.username}, {self.email}'
修改完模型后需要执行数据库迁移操作,同步更新表结构。
修复后效果
提交重复用户名/邮箱时,表单校验会直接拦截请求,不会执行数据库插入逻辑,对应字段下方会正常展示预设的错误提示,不会再抛出数据库IntegrityError错误页面。
内容的提问来源于stack exchange,提问作者Addy
相关产品推荐
相关产品推荐

