pythonvalidationflask-wtformsauto-populate

When submitting a Flask WTForms, how can fields be left blank without blanking-out those values from the database table?


Question:

I'm working on a Flask app w/ a WTForms EditProfileForm containing: First Name, Last Name, Username, and About Me. The form loads w/ Username and About Me auto-populated. First Name and Last Name are blank. If the user edits the About Me TextAreaField, but leaves the First Name and Last Name fields blank, submitting the form overwrites those DB fields as blank.

Related Code Examples:

forms.py - EditProfileForm class:

class EditProfileForm(FlaskForm):
    firstname = StringField('First Name')
    lastname = StringField('Last Name')
    username = StringField('Username', validators=[DataRequired()])
    about_me = TextAreaField('About Me', validators=[Length(min=0, max=140)])
    submit = SubmitField('Submit')

    def __init__(self, original_username, *args, **kwargs):
        super(EditProfileForm, self).__init__(*args, **kwargs)
        self.original_username = original_username

    def validate_username(self, username):
        if username.data != self.original_username:
            user = User.query.filter_by(username=self.username.data).first()
            if user is not None:
                raise ValidationError('Please choose a different username.')

routes.py - the edit_profile view

@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
    form = EditProfileForm(current_user.username)
    if form.validate_on_submit():
        current_user.firstname = form.firstname.data
        current_user.lastname = form.lastname.data
        current_user.username = form.username.data
        current_user.about_me = form.about_me.data
        db.session.commit()
        flash(_('Your changes have been saved.'))
        return redirect(url_for('edit_profile'))
    elif request.method == 'GET':
        form.username.data = current_user.firstname
        form.username.data = current_user.lastname
        form.username.data = current_user.username
        form.about_me.data = current_user.about_me
    return render_template('edit_profile.html', title=_('Edit Profile'),
                           form=form)

models.py - the User class

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    firstname = db.Column(db.String(50), index=True)
    lastname = db.Column(db.String(50), index=True)
    username = db.Column(db.String(50), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    about_me = db.Column(db.String(140))

    def __repr__(self):
        return '<User {}>'.format(self.username)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

edit_profile.html

   {% extends "base.html" %}
    {% import 'bootstrap/wtf.html' as wtf %}

    {% block app_content %}
        <h1>Edit Profile</h1>
        <div class="row">
            <div class="col-md-4">
                {{ wtf.quick_form(form) }}
            </div>
        </div>
    {% endblock %}

Solution

  • Turns out, the problem was a simple copy/paste error that I made in the routes.py edit_profile view function. I had this...

        form.username.data = current_user.firstname
        form.username.data = current_user.lastname
        form.username.data = current_user.username
        form.about_me.data = current_user.about_me
    

    I had form.username.data in three out of the four field. Doh!