简体   繁体   English

RadioFields的Flask-WTF FieldList不允许动态默认值

[英]Flask-WTF FieldList of RadioFields not allowing dynamic default value

I am working on building a small survey system to meet some specific requirements for a research project I'm working on.我正在构建一个小型调查系统,以满足我正在从事的研究项目的一些特定要求。 One of those requirements is the user has to be able to save their place and be able to return to the survey later and finish their work.其中一项要求是用户必须能够保存他们的位置并能够稍后返回调查并完成他们的工作。

I am using Flask and Flask-WTF to make this system, and I'm using a MySQL database to save the users' responses.我正在使用 Flask 和 Flask-WTF 来制作这个系统,并且我正在使用 MySQL 数据库来保存用户的回复。 I've run into an issue where I am not able to have default values when using a FieldList of RadioFields, where my goal is to dynamically set this default value for the questions that have been answered to allow the user to see/modify their previous work.我遇到了一个问题,在使用 RadioFields 的 FieldList 时我无法拥有默认值,我的目标是为已回答的问题动态设置此默认值,以允许用户查看/修改他们以前的问题工作。

Here are the forms I've built, the html template, and the current output:这是我构建的forms,html模板,以及当前的output:

class Phrase(db.Model):
    __tablename__ = 'phrases'
    id            = db.Column(db.Integer, primary_key=True, nullable=False)
    phrase_text   = db.Column(db.Text, nullable=False)

...

class SinglePhraseForm(Form):
    phrase_rating = RadioField()
    phrase_cat    = SelectField("Please choose whether this phrase is more useful in written or spoken communication:", choices=[('w', 'Written'), ('s', 'Spoken')])

class AllPhrasesForm(FlaskForm):
    phrases = FieldList(FormField(SinglePhraseForm), min_entries=5)

...

@app.route("/phrases", methods=["GET", "POST"])
def phrases():
    phrases_query = Phrase.query.all()

    form = AllPhrasesForm()
    for i in range(len(phrases_query)):
        form.phrases[i].phrase_rating.label = f"Please rate how useful the following phrase is: {phrases_query[i].phrase_text}"
        form.phrases[i].phrase_rating.choices = [('1', '1 - Not useful'), ('2','2'), ('3','3'), ('4','4'), ('5','5 - Very Useful')]
        form.phrases[i].phrase_rating.default = str((i % 5) + 1)

    ### THIS CODE WORKS AND RENDERS DEFAULT CORRECTLY ###
    # form = SinglePhraseForm()
    # form.phrase_rating.label = "THIS IS A TEST LABEL"
    # form.phrase_rating.choices = [('t1', 'TEST1'), ('t2', 'TEST2'), ('t3', 'TEST3')]
    # form.phrase_rating.default = 't2'
    # form.process()

    if form.validate_on_submit():
        if form.continue_survey.data:
            return redirect(url_for("extended_responses"))

    return render_template("phrases.html", form=form)
<h1>Phrases</h1>

<form method="POST" action="/phrases">
    {{ form.hidden_tag() }}
    {% for phrase in form.phrases %}
        {% for subfield in phrase if subfield.widget.input_type != "hidden" %}
            <tr>
                <td>{{ subfield.label }}</td>
                <td>{{ subfield() }}</td>
            </tr>
        {% endfor %}
        <br>
    {% endfor %}
    <!-- TEST FOR SINGLE FORM -->
    <!-- {% for subfield in phrase if subfield.widget.input_type != "hidden" %}
        <tr>
            <td>{{ subfield.label }}</td>
            <td>{{ subfield() }}</td>
        </tr>
    {% endfor %} -->
</form>

In the phrases route, I am trying to iterate over the RadioFields and dynamically set their label, choices, and default values.在短语路由中,我试图遍历 RadioFields 并动态设置它们的 label、选项和默认值。 The label and choices correctly render, but the default value is not showing up: Screenshot of the first two questions with dummy data . label 和选项正确呈现,但未显示默认值:前两个问题的屏幕截图与虚拟数据 I've tried following answers like this one , but using form.process() simply removes all of my dynamic values: Screenshot of the form after using form.process() , which is not what I want.我已经尝试过像这样的答案,但是使用form.process()只会删除我所有的动态值:使用 form.process() 后的表单截图,这不是我想要的。 I tried exploring using my SinglePhraseForm (changing the base class from Form to FlaskForm) and am actually able to get all the values in the commented out code to render, including default, using form.process() and the commented out code in the html template.我尝试使用我的 SinglePhraseForm 进行探索(将基础 class 从 Form 更改为 FlaskForm),并且实际上能够使用form.process()和 html 中的注释掉代码中的所有值来渲染,包括默认值模板。 This makes me things I'm doing something wrong with the FieldList.这让我觉得我在 FieldList 上做错了什么。

Any guidance on how I can correctly render dynamic default values for this FieldList of RadioFields?关于如何正确呈现此 RadioFields 的 FieldList 的动态默认值的任何指导?

This looks like what you need!这看起来像你需要的! ( I hope so ) 我希望如此


  • The only drawback, which I still don't know how to fix, is that CTRL + R (arrow refresh button of browser) doesn't clear cache, unlike CTRL + F5 does.唯一的缺点,我仍然不知道如何解决,是CTRL + R (浏览器的箭头刷新按钮)不会清除缓存,不像CTRL + F5那样。 You will have to fix that on your own (if that even matters)你必须自己解决这个问题(如果这很重要的话)
  • Also, I used a list of Query objects that emulate your database values (correct me if I did that wrong)另外,我使用了一个 Query 对象列表来模拟您的数据库值(如果我做错了,请纠正我)
  • Pass coerce=int to RadioField so it will accept integer values (your database id Column is Integer)coerce=int传递给RadioField以便它接受 integer 值(您的数据库 id 列是整数)
  • Number of entries in FieldList is depending on phrases_query length ( kinda dynamic ) FieldList中的条目数取决于phrases_query长度(有点动态

Here is the code:这是代码:

main.py:主要.py:

from flask import Flask, render_template
from wtforms.fields import RadioField, SelectField, FieldList, FormField, SubmitField
from flask_wtf import FlaskForm


app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret_key'


class Query:
    def __init__(self, id, phrase_text) -> None:
        self.id = id
        self.phrase_text = phrase_text


phrases_query = [
    Query(1, 'I hope this helps'),
    Query(2, 'Yeah that works, finally'),
    Query(3, 'I spent 4 hours and its 5 am, bruh xd'),
    Query(4, 'Alarm set on 8am... (yeah, I"m ranting in my code Lorem Ipsum, like, gona stop me or smtn? :D)'),
    Query(5, 'MYRID'),
]


class SinglePhraseForm(FlaskForm):
    phrase_rating = RadioField(coerce=int)
    phrase_cat = SelectField(
        "Please choose whether this phrase is more useful in written or spoken communication:",
        choices=[
            ('w', 'Written'),
            ('s', 'Spoken'),
        ],
    )


entries_amount = len(phrases_query)


class AllPhrasesForm(FlaskForm):
    global entries_amount
    phrases = FieldList(
        FormField(SinglePhraseForm),
        min_entries=entries_amount,
    )
    submit = SubmitField(label="save all the select field values")


phrase_rating_choices = [
    (1, '1 - Not useful'),
    (2, '2'),
    (3, '3'),
    (4, '4'),
    (5, '5 - Very Useful'),
]


def generate_my_form(overwrite_data=True):
    form = AllPhrasesForm()
    for i in range(len(phrases_query)):
        form.phrases[i].phrase_rating.label = \
            f"Please rate how useful the following phrase is: {phrases_query[i].phrase_text}"
        global phrase_rating_choices
        form.phrases[i].phrase_rating.choices = phrase_rating_choices
        if overwrite_data:
            form.phrases[i].phrase_rating.data = (i % 5) + 1
    return form


@app.route("/phrases", methods=["GET", "POST"])
def phrases():
    form_for_validation = generate_my_form(overwrite_data=False)
    if form_for_validation.validate_on_submit():
        # do something with form_for_validation.data
        print(form_for_validation.data)

        # render page with chosen by user form data
        return render_template(
            "phrases.html",
            form=form_for_validation,
        )
    else:
        form_with_default_data = generate_my_form(overwrite_data=True)
        # render page with default form data
        return render_template(
            "phrases.html",
            form=form_with_default_data,
        )


if __name__ == "__main__":
    app.run(debug=True)

phrases.html:短语.html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Answer</title>
</head>

<body>
    <h1>Phrases</h1>

    <form method="POST" action="/phrases">
        {{ form.hidden_tag() }}
        {% for subfield in form if subfield.widget.input_type != "hidden" %}
        <tr>
            <td>{{ subfield() }}</td>
        </tr>
        {% endfor %}
    </form>

</body>

</html>

I took this answer and adjusted it a bit.我接受了这个答案并对其进行了一些调整。 It's weird how it has so few upvotes.奇怪的是,它的赞成票如此之少。 I've spend a lot of time trying to set default parameter before instancing a form, but it didn't work for me, eventually because of UnboundField.我花了很多时间尝试在实例化表单之前设置默认参数,但它对我不起作用,最终是因为 UnboundField。 And FieldList prevent from looping over it's arguments.并且 FieldList 防止循环它的 arguments。


I wonder if that helped.我想知道这是否有帮助。 If not - feel free to write back, I'm curious now:D如果没有 - 请随时回信,我现在很好奇:D

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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