Flask表单外部自定义验证器报错,如何修正代码?
Flask自定义验证器引发sqlite3.InterfaceError问题
问题背景
我尝试在RegistrationForm外部创建自定义验证器check_if_username_not_in_db,但运行时在if form.validate_on_submit():语句触发sqlite3.InterfaceError(绑定参数0时出错,可能是不支持的类型)。我怀疑应该使用form.username.data而非username.data,但不知道如何在外部验证器中传入form对象,同时希望该验证器保持在表单类外部。
错误追踪信息
Traceback (most recent call last): File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 1819, in _execute_context self.dialect.do_execute( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\default.py", line 732, in do_execute cursor.execute(statement, parameters) sqlite3.InterfaceError: Error binding parameter 0 - probably unsupported type. The above exception was the direct cause of the following exception: Traceback (most recent call last): File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 2091, in __call__ return self.wsgi_app(environ, start_response) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 2076, in wsgi_app response = self.handle_exception(e) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 2073, in wsgi_app response = self.full_dispatch_request() File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 1518, in full_dispatch_request rv = self.handle_user_exception(e) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 1516, in full_dispatch_request rv = self.dispatch_request() File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask\app.py", line 1502, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args) File "C:\Users\user\OneDrive\Desktop\flaskcodeusethis\flaskblog2\app\auth\routes.py", line 152, in register if form.validate_on_submit(): File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\flask_wtf\form.py", line 86, in validate_on_submit return self.is_submitted() and self.validate() File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\wtforms\form.py", line 318, in validate return super(Form, self).validate(extra) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\wtforms\form.py", line 150, in validate if not field.validate(self, extra): File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\wtforms\fields\core.py", line 226, in validate stop_validation = self._run_validation_chain(form, chain) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\wtforms\fields\core.py", line 246, in _run_validation_chain validator(form, self) File "C:\Users\user\OneDrive\Desktop\flaskcodeusethis\flaskblog2\app\auth\functions.py", line 107, in check_if_username_not_in_db if User.query.filter_by(username=username.data).first(): File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\orm\query.py", line 2819, in first return self.limit(1)._iter().first() File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\orm\query.py", line 2903, in _iter result = self.session.execute( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\orm\session.py", line 1712, in execute result = conn._execute_20(statement, params or {}, execution_options) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 1631, in _execute_20 return meth(self, args_10style, kwargs_10style, execution_options) File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\sql\elements.py", line 332, in _execute_on_connection return connection._execute_clauseelement( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 1498, in _execute_clauseelement ret = self._execute_context( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 1862, in _execute_context self._handle_dbapi_exception( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 2043, in _handle_dbapi_exception util.raise_( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\util\compat.py", line 208, in raise_ raise exception File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\base.py", line 1819, in _execute_context self.dialect.do_execute( File "C:\Users\user\Anaconda3\envs\py\lib\site-packages\sqlalchemy\engine\default.py", line 732, in do_execute cursor.execute(statement, parameters) sqlalchemy.exc.InterfaceError: (sqlite3.InterfaceError) Error binding parameter 0 - probably unsupported type. [SQL: SELECT user.id AS user_id, user.username AS user_username, user.hashed_password AS user_hashed_password, user.email AS user_email, user.registration_confirmation_email AS user_registration_confirmation_email, user.profile_pic_name AS user_profile_pic_name FROM user WHERE user.username = ? LIMIT ? OFFSET ?] [parameters: ({'username': 'arjipajgrpjg', 'email': 'fowomek545@fesgrid.com', 'password': 'arjipajgrpjg', 'confirm_password': 'arjipajgrpjg', 'csrf_token': 'IjdhMjA1OTU0ZWJiZGVlYmY5YzI5NDllNTlkOGY2ZWM2NmJhMzg5MmIi.ZRj5wg.8oHbeEs6RH2JNylJDMg7z9IhvpA'}, 1, 0)]
相关代码
auth/functions.py
# custom validator def check_if_username_not_in_db(username, self): if the username is not in the db the code works, if not it raises an ValidationError. This runs in the RegistrationForm in username column ''' if User.query.filter_by(username=username.data).first(): raise ValidationError('The username is already taken. Please select another username for registration.') # okay wording? else: flash('Success the username is not taken and you can successfully register.') return None
auth/forms.py
class RegistrationForm(FlaskForm): ''' This is in /register route. The forms are username, email, password and confirm_password ''' username = StringField('Username',validators= [ DataRequired(message='Username is required'), Length(min=2, max=25 , message='Must be between 2 and 25 characters'), check_if_username_not_in_db
routes.py
@auth.route("/register", methods = ['POST', 'GET']) def register(): # if the user is logged in make so they can't go to the register page. if current_user.is_authenticated: return redirect(url_for(('auth.home'))) form = RegistrationForm() # form.validate_on_submit(): are always the same line of render template to always allow a get request. if form.validate_on_submit(): username_form = form.username.data # more code ... return render_template('register.html',title='register', form=form)
解决方案
问题根源
WTForms自定义验证器的标准参数顺序是**(form, field)**,你写反了参数顺序:把username(实际传入的是整个表单对象)和self(实际传入的是当前字段对象)搞混了。导致你调用username.data时,取到的是整个表单的data属性(一个字典),把字典传给SQLAlchemy查询参数,就触发了类型不支持的错误。
修复步骤
- 修正验证器参数顺序:将
check_if_username_not_in_db(username, self)改为check_if_username_not_in_db(form, field),通过field.data获取当前输入的用户名。 - 调整逻辑位置:把
flash成功提示移到路由中,避免验证器每次校验(包括GET请求)都触发提示。
修正后的代码
auth/functions.py
from wtforms.validators import ValidationError from ..models import User # 根据项目结构调整导入路径 # 自定义验证器:检查用户名是否已存在 def check_if_username_not_in_db(form, field): if User.query.filter_by(username=field.data).first(): raise ValidationError('该用户名已被占用,请选择其他用户名注册。')
auth/forms.py
from flask_wtf import FlaskForm from wtforms import StringField from wtforms.validators import DataRequired, Length from .functions import check_if_username_not_in_db class RegistrationForm(FlaskForm): """注册表单:包含用户名、邮箱、密码、确认密码字段""" username = StringField('用户名', validators=[ DataRequired(message='用户名不能为空'), Length(min=2, max=25, message='用户名长度必须在2到25个字符之间'), check_if_username_not_in_db ]) # 补充邮箱、密码等其他字段
routes.py
from flask import flash, redirect, url_for, render_template from flask_login import current_user from . import auth from .forms import RegistrationForm @auth.route("/register", methods=['POST', 'GET']) def register(): if current_user.is_authenticated: return redirect(url_for('auth.home')) form = RegistrationForm() if form.validate_on_submit(): # 验证通过后再提示成功 flash('用户名可用,可继续完成注册!', 'success') username_form = form.username.data # 后续创建用户的逻辑... return redirect(url_for('auth.login')) return render_template('register.html', title='注册', form=form)
内容的提问来源于stack exchange,提问作者jrgop
相关产品推荐
相关产品推荐

