0

Someone wrote a page which lets you fill out a form and do something one at a time, I am working on a another page with a "bulk" functionality, which needs two fewer form fields.

I have been trying to recycle their Form object rather than define a new one which is only very slightly different.

Can explain this with the example from the docs: https://flask.palletsprojects.com/en/1.0.x/patterns/wtforms/

The form object is created:

from wtforms import Form, BooleanField, StringField, PasswordField, validators

class RegistrationForm(Form):
    username = StringField('Username', [validators.Length(min=4, max=25)])
    email = StringField('Email Address', [validators.Length(min=6, max=35)])
    password = PasswordField('New Password', [
        validators.DataRequired(),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('Repeat Password')
    accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])

There is an endpoint:

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User(form.username.data, form.email.data,
                    form.password.data)
        db_session.add(user)
        flash('Thanks for registering')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

and register.html (part of it below)

<form method=post>
  <dl>
    {{ render_field(form.username) }}
    {{ render_field(form.email) }}
    {{ render_field(form.password) }}
    {{ render_field(form.confirm) }}
    {{ render_field(form.accept_tos) }}
  </dl>
  <p><input type=submit value=Register>
</form>

Now say you are making another page which does not need the email field but needs all the other fields. If you delete the line:

{{ render_field(form.email) }}

out of the html, the page will render okay, but when the user clicks submit, form.validate() will be False and so it will not submit.

How can you give the form object something in the .py file so that it ignores a field which failed to be sent across from the browser?

Tried

form.email = "field not needed here"

...before the validation step, but it didn't seem to work

cardamom
  • 6,873
  • 11
  • 48
  • 102

2 Answers2

1

I guess that you want something like this:

The form object:

from wtforms import Form, BooleanField, StringField, PasswordField, validators

class CustomForm(Form):
    username = StringField('Username', [validators.Length(min=4, max=25)])
    password = PasswordField('New Password', [
        validators.DataRequired(),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('Repeat Password')
    accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])

Your new endpoint, let's say we want to create a User without email:

@app.route('/endpoint', methods=['GET', 'POST'])
def register():
    form = CustomForm(request.form)
    if request.method == 'POST' and form.validate():
        user = UserWithoutEmail(form.username.data, form.password.data)
        db_session.add(user)
        flash('Thanks for registering')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

and register.html to show everything but email:

<form method=post>
  <dl>
    {{ render_field(form.username) }}
    {{ render_field(form.password) }}
    {{ render_field(form.confirm) }}
    {{ render_field(form.accept_tos) }}
  </dl>
  <p><input type=submit value=Register>
</form>
bhito
  • 2,083
  • 7
  • 13
0

I have found a tidy solution adapted from here

class RegistrationForm2(RegistrationForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        delattr(self, 'email')

I like it cause it does not involve repeating the whole class again. Not sure if it is more or less elegant than filling in fake values in the .py form and avoiding defining a new form but it works.

cardamom
  • 6,873
  • 11
  • 48
  • 102