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

132 views Asked by At

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': '[email protected]', '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)
1

There are 1 answers

3
Detlef On BEST ANSWER

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())