简体   繁体   中英

Validation on dynamically sized FieldList in WTForms

I am creating a quiz webapp using Flask and WTForms in which the user is shown a quiz which they can answer. The questions for the quiz come from a DB and hence the form to let the users answer the quiz is dynamic. Here is how the form class looks like:

class QuizForm(FlaskForm):
    answers = FieldList(RadioField('Correct Answer', choices=CHOICES,
                                   validators=[DataRequired()]),
                        min_entries=0)
    submit = SubmitField('Submit your answers')

Before I serve the form, I append entries to the field list as follows:

questions = Question.query.all()
form = QuizForm()
for i, question in enumerate(questions):
    form.answers.append_entry()
    choices = [('1', question.option_a),
               ('2', question.option_b),
               ('3', question.option_c),
               ('4', question.option_d)]
    form.answers.entries[i].choices = choices

This works well and creates the form as I would expect. But, I am not able to get validation to work where I want to make sure the user has answered all questions. This is how I am currently validating the form:

questions = Question.query.all()
form = QuizForm(request.form)
if request.method == 'POST':
    if form.validate() and len(form.answers.entries) == len(questions):
        # use form data

The problems I have are:

  • I would ideally want the validation to work just using form.validate() .
  • In the current approach, the values for field.errors are not set, and thus I can't show the user a message that a field is required right below the radio field containing the options for the question (using WTForms validation).

The full code can be seen at github .

What is a good way to implement validation for this use case?

As commented further info in questions: stackoverflow.com/questions/50882720 and stackoverflow.com/questions/51048153

Personally I would use the dynamic form method to assign a unique name to each RadioField required so that the result would be similar to, for example if the form was statically created:

class WebForm(FlaskForm):
    q_1 = RadioField('Q 1', validators=[InputRequired()], ...)
    q_2 = RadioField('Q 2', validators=[InputRequired()], ...)

Then the validators will definitely work as requested.

Failing that I suppose you could just write your own validator over your FieldList named answers . Here is an example of a custom validator catching an input field supposed to capture a full name and it breaks if it doesn't detect a space between names:

from wtforms.validators import ValidationError
class WebForm(FlaskForm):
    fullname = StringField('Input full name', validators=[])
    def validate_fullname(self, field):
        if ' ' not in field.data:
            raise ValidationError('Please give first and last name')

If you write your own validator on the FieldList you can perhaps loop through and check each has some data, but the more painful approach (in my opinion) is why I stated my preferred way of doing this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM