Python/Flask-WTF - How can I randomize the choices

2019-08-02 13:02发布

问题:

I am looking to build a multiple choice quiz using python/flask/flask-wtf. I am successfully able to pull random questions from a database as well as pull random choices as the possible answers. I am using a for loop on the template page that first displays the question and then the possible answers.

Here is my template.

<div class="quote-block">
{% for quote in quotes %}
    {{ quote.line_text }}
    <form method="POST">
        {{ form.hidden_tag() }}
        {{ form.question }}
        <input type="submit">
    </form>
{% endfor %}
</div>

My problem is that the possible answers are the same for each question and in the same order. I understand why this is happening. The query into the database for the RadioField choices is only happening once. And then the results of that query are being pulled for each instance of the for loop.

Here is my view route.

@app.route('/quiz/', methods=['POST', 'GET'])
def quiz():
    quotes = Quotes.query.order_by(func.rand()).limit(5)
    form = Quiz()
    form.question.choices = [(choice.speaker_id, choice.speaker) for choice in Characters.query.order_by(func.rand()).limit(4)]
    return render_template('television/quiz.html', form=form, quotes=quotes, options=options)

And my form.

class Quiz(FlaskForm):
    question = RadioField('Label', choices=[])

Like I said, all of this works. I just can't figure out how to start a new choices query for each question. Any help would be greatly appreciated. Thanks!

回答1:

You could try dynamically extending your FlaskForm:

def Quiz(number_of_questions):
    class _Quiz(FlaskForm):
        pass
    for n in range(number_of_questions):
        setattr(_Quiz, RadioField('Label', choices=[]), 'question_' + str(n))
    return _Quiz()

Now your form has a question_[n] attribute for each question so you can iterate it in jijna2:

<div class="quote-block">
<form method="POST">
{{ form.hidden_tag() }}
{% for q_num in range(n) %}
    {{ quotes[q_num].line_text }}
    {{ form['question_' + q_num|string }}
{% endfor %}
<input type="submit">
</form>
</div>


回答2:

After a lot of reading and research, I realized I was trying to do too much too soon. So I took a step back and built the blocks of what I was trying to do and then it finally came together. Of course, I am having new issues with the form validation portion, but I'll include that in a new question.

My biggest roadblock was that I was trying to pull random choice options for the RadioField. Instead, I added three columns to the database table and supplied the options in the table. That made it easier to pull the options and the question with one query.

Basically, I am running a for loop over the fields in the form. If the field is a RadioField, it runs a query against the database and pulls a random row. I then use another for loop over the pulled row and assign different elements to the pieces of the RadioField (label, choices).

If you know of a more elegant way to do this, I'd love to hear it. But for now it works.

My form and template stayed the same. But here is my new route view.

    @app.route('/quiz/', methods=['GET', 'POST'])
    def quiz():
        form = Quiz()
        for field in form:
            if field.type != "RadioField":
                continue
            else:
                pulls = Quotes.query.order_by(func.rand()).limit(1)
                for pull in pulls:
                    answer = pull.speaker
                    option_two = pull.option_two
                    option_three = pull.option_three
                    option_four = pull.option_four
                    question = pull.line_text
                    field.label = pull.line_text
                    field.choices = [(answer, answer), (option_two, option_two), (option_three, option_three), (option_four, option_four)]
            return redirect(url_for('you_passed'))
        return render_template('television/quiz.html', form=form)