flaskflask-wtformswtforms

I am trying to create a custom validator outside the form but I am running into a error. How do I fix the code?


I think the problem is username.data should be form.username.data but I don't know how to add form in check_if_username_not_in_db. I also want the functions to be outside RegistrationForm.

The error is located below. The error is triggered by the line
if form.validate_on_submit():

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)]
(Background on this error at: https://sqlalche.me/e/14/rvf5)

Here is the documentation on custom validators https://wtforms.readthedocs.io/en/2.3.x/validators/

Here is the code.

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)

Solution

  • If you write your validator this way, note that the first argument is the form and the second is the input field.

    The following code should fix your problem.

    def check_if_username_not_in_db(form, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('The username is already taken.')
    

    Here is the complete code of my stripped down example.

    from flask import (
        Flask, 
        render_template, 
        request
    )
    from flask_sqlalchemy import SQLAlchemy
    from flask_wtf import FlaskForm
    from wtforms import StringField
    from wtforms.validators import DataRequired, Length, ValidationError
    
    app = Flask(__name__)
    app.config.from_mapping(
        SECRET_KEY='your secret here', 
        SQLALCHEMY_DATABASE_URI='sqlite:///users.db'
    )
    db = SQLAlchemy(app)
    
    class User(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String, unique=True)
    
    def check_if_username_not_in_db(form, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('The username is already taken.')
    
    class RegistrationForm(FlaskForm):
        username = StringField('Username', 
            validators=[
                DataRequired(), 
                Length(min=2, max=25), 
                check_if_username_not_in_db
            ]
        )
    
    with app.app_context():
        db.drop_all()
        db.create_all()
        user = User(username='jrgop')
        db.session.add(user)
        db.session.commit()
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        form = RegistrationForm(request.form)
        if form.validate_on_submit():
            user = User()
            form.populate_obj(user)
            db.session.add(user)
            db.session.commit()
        return render_template('register.html', **locals())