I want my ValidationError to say "username already taken," but I get an Integrity Error(Flask)

50 Views Asked by At

Below is the registration form python file: I'm following a tutorial by Corey Shafer: https://www.youtube.com/watch?v=803Ei2Sq-Zs&t=269s , and I basically followed every step so far, except for the bootstrap. How can I return "username already taken" when I try to sign up with the same username?

I'm not exactly sure what to do, looked up a bunch of answers but haven't really found anything.

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField, BooleanField
from wtforms.validators import DataRequired, Length, Email, EqualTo, ValidationError
from flask_pract.models import User

class Reg_Form(FlaskForm):
    username = StringField('Username', validators=[DataRequired(),Length(min=5,max=20)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Sign Up')

def validate_username(self, username):
    user = User.query.filter_by(username = username.data).first()
    if user:
        raise ValidationError('That username is taken. Please choose a different one.')
    
def validate_email(self, email):
    user = User.query.filter_by(username = email.data).first()
    if user:
        raise ValidationError('That email is taken. Please choose a different one.')


class Login_Form(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember = BooleanField('Remember Me')
    submit = SubmitField('Login')

class UpdateAccountForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired(),Length(min=5,max=20)])
    email = StringField('Email', validators=[DataRequired(), Email()])
    submit = SubmitField('Sign Up')

Then routes python file here:

from flask import render_template, url_for, flash, redirect, request
from flask_pract import app, bcrypt, db
from flask_pract.reg_forms import Reg_Form, Login_Form
from flask_pract.models import User,Post
from flask_login import login_user, current_user, logout_user, login_required

@app.route("/")
def home():
    return render_template('home.html')

@app.route("/about")
def about():
    return render_template('about.html',title="About")

@app.route("/register", methods=['GET','POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('home'))
    form = Reg_Form()
    if form.validate_on_submit():
        hashed_pw = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
        user = User(username = form.username.data, email=form.email.data, password = hashed_pw)
        db.session.add(user)
        db.session.commit()
        flash('Your account has been successfully created! You are now able to log in.')
        return redirect(url_for('login'))
    return render_template('register.html',title="Register", form = form)

@app.route("/login", methods=['GET','POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('home'))
    form = Login_Form()
    if form.validate_on_submit():
        user = User.query.filter_by(email = form.email.data).first()
        if user and bcrypt.check_password_hash(user.password, form.password.data):
            login_user(user,remember=form.remember.data)
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('account'))
        else:
            flash(f'Login unsuccessful. Please check your email and password')
            
    return render_template('login.html',title="Login", form = form)

@app.route("/logout")
def logout():
    logout_user()
    return redirect(url_for('home'))

@app.route("/account")
@login_required
def account():
    image_file = url_for('static', filename='profile_pics/' + current_user.image_file)
    return render_template('account.html', title ='Account',image_file=image_file )

Registration html:

{% extends "layout.html" %}
{% block content %}
<h1>It's the register page</h1>
<form method="POST" action=""> 
    {{ form.hidden_tag() }}
    {{form.username.label }}
    {{form.username}}

    {{form.email.label}}
    {{form.email }}

    {{form.password.label}}
    {{form.password }}


    {{form.confirm_password.label}}
    {{form.confirm_password}}


    {{form.submit}}

    {% for field, errors in form.errors.items() %}
    <small class="form-text text-muted ">
        {{field}}
        {{ ', '.join(errors) }}
    </small>
    {% endfor %}


</form>
<h1><a href="{{ url_for('login') }}">Already have an account?</a></h1>
{% endblock content %}

And models:

from datetime import datetime
from flask_pract 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)
    email = db.Column(db.String(120), unique = True, nullable = False)
    image_file = db.Column(db.String(20), nullable = False, default='default.jpg')
    password = db.Column(db.String(60), nullable = False)
    posts = db.relationship('Post',backref='author', lazy=True)

    def __repr__(self):
        return f"User('{self.username}','{self.email}','{self.image_file}')"

class Post(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    title = db.Column(db.String(100), nullable = False)
    date_posted = db.Column(db.DateTime, nullable = False, default = datetime.utcnow)
    content = db.Column(db.Text, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable = False)

    def __repr__(self):
        return f"Post('{self.title}','{self.date_posted}')"
1

There are 1 best solutions below

0
Mark Kariuki On

Add the following line in the register route after the if form.validate_on_submit(): statement:

if User.query.filter_by(username=form.username.data).first():
    flash('Username already taken', 'warning')
    return redirect(url_for('register')

Then remove the validations from the form file.