Search code examples
flaskflask-sqlalchemyflask-wtforms

Flask Form Validation not Working on Update


I have a flask form that is tied to a model with a unique constraint. My create form/validation works as expected and presents an error to the user if a name collides with one already in the database. However, when I try to do the same thing to validate an update action (where the name might be resubmitted, and if it matches it's previous value, be allowed to pass, but throw the error if it conflicts with another record). I cannot figure out how to get it to do this.

Here is the form that works correctly:

class GearCategoryForm(FlaskForm):
    name = StringField('Category Name:', validators=[InputRequired()])
    desc = StringField('Category Description:')
    submit = SubmitField('Create Category')

    def validate_name(self, name):
        category = GearCategories.query.filter_by(name=self.name.data.capitalize()).first()
        if category is not None:
            raise ValidationError('Category already exists')

And here is the one that I cannot get working:

class UpdateGearCategoryForm(FlaskForm):
    name = StringField('Category Name:', validators=[InputRequired()])
    desc = StringField('Category Description:')
    submit = SubmitField('Update Category')

    def validate_name(self, name):
        category = GearCategories.query.filter_by(name=self.name.data.capitalize()).first()
        #flash(f"{category} --- {name.data}") --- debugging attempts
        if category is not None and category.name != name.data:
            raise ValidationError('Category already exists')

Here is the route from the form that is NOT working:

@admin_gear_bp.route('/admin/gear/category/<id>/update', methods=['GET', 'POST'])
@login_required
def update_category(id):
    if not current_user.is_anonymous:

        if current_user.is_admin:
            try:
                category = GearCategories.query.get_or_404(id)

            except SQLAlchemyError as error:
                return render_template('errors/500.html', title='Internal Error'), 500

            form = UpdateGearCategoryForm()
        
            if form.validate_on_submit():
                category.name = form.name.data.capitalize()
                category.description = form.desc.data.capitalize()
                db.session.commit()
                flash('Successfully updated category {}'.format(category.name))
                return redirect(url_for('admin_gear.categories_admin'))
        
            form.name.data = category.name
            form.desc.data = category.description
                
            return render_template('admin/gear/add_category.html', title='Update Category', form=form,
                                       legend='Update Category'), 200
            
        else:
            return render_template('errors/401.html', title='Unauthorized'), 401
        
    else:
        flash('You must login to update gear categories.')
        return redirect(url_for('users.login'))

I'm expecting to get an error presented to the user if the value on update matches a pre-existing record BUT NOT if it matches what the record itself previously was.


Solution

  • I think I have it working now, at least my test cases are passing. FWIW, below is the updated 'update' form where the validation works as expected.

    class UpdateGearCategoryForm(FlaskForm):
        name = StringField('Category Name:', validators=[InputRequired()])
        desc = StringField('Category Description:')
        submit = SubmitField('Update Category')
    
        def validate_name(self, name):
            category = GearCategories.query.filter_by(name=self.name.data.capitalize()).first()
            if category is not None:
                if name.data != category.name.capitalize():
                    raise ValidationError('Category already exists')