如何为WTForms的SelectField设置默认值?

69

使用WTForms设置SelectField的默认值时,可以通过将值传递给“default”参数来实现。

class TestForm(Form):
  test_field = SelectField("Test: ", choices=[(1, "Abc"), (2, "Def")], default=2)

我也尝试了以下方法。

class TestForm(Form):
  test_field = SelectField("Test: ", choices=[(1, "Abc"), (2, "Def")], default=(2, "Def"))

不要将默认选择的字段设置为"Def."。对于其他类型的字段(例如TextField),这很有效。如何设置SelectField的默认值?

9个回答

50

我认为这个问题是由于Fielddata 属性覆盖了default,并用WTForms无法理解的内容进行了替换(例如一个数据库模型对象 - 它期望一个int)。如果您在构造函数中像这样填充表单,则会发生这种情况:

form = PostForm(obj=post)

解决方案是在表单填充后手动设置data属性:

form = PostForm(obj=post)
form.category.data = (post.category.id
                      if page.category
                      else 0) # I make 0 my default

9
在运行时从视图动态地填充选项时,以下方法可以使其中一个选项成为默认选择。由于此问题受到众多谷歌用户的关注,这可能应该被接受作为答案。 - Aaron D
3
我注意到当我在视图中使用列表中的另一选项更改默认选项并提交表单时,提交的是默认选项而不是新选项。如何解决这个问题? - nikitz
1
这似乎不再起作用了。当我更新字段时,原始的choices=choicesfunction()仍然被调用。因此,更新字段没有效果。 更新 form.category.choices = [(),()] 对我有用。 - Karl

46

Flask-WTF 0.14.2 的用户。这个答案适用于有类似问题的人。

基本上,之前的解决方案都不能很好地与form.validate_on_submit()一起使用。

设置form.test_field.data确实会在页面加载时更改默认值,但是在validate_on_submit之后数据仍然保持不变(浏览器中的用户更改没有效果)。

如果设置form.test_field.default然后调用form.process(),也可以在页面加载时更改值,但是validate_on_submit会失败。

以下是新的解决方法:

class TestForm(Form):
    test_field = SelectField("Test", choices=[(0, "test0"), (1, "test1")])

@app.route("/test")
def view_function():
    form = TestForm(test_field=1)
    if form.validate_on_submit():
        ...

1
是的。这就是有效的方法。换句话说,要在selectlist中选择特定选项,必须在表单实例化时将该选项加载到表单中。同样,在multipleselectlist中选择多个选项也必须在表单实例化时将这些选项加载到表单中。设置selectlist的语法因其类型而异:1)selectlist示例:form = MyUserForm(roles = 1)#第一个选项被预先选择2)selectmultiplelist示例:form = MyUserForm(roles = [1,3])#第一个和第三个选项被预先选择 - Luke
1
也适用于我!Flask-WTF == 0.14.3 - Dragos Vasile

41

你发布的第一种方法是正确的,对我也起作用。它不起作用的唯一解释可能是你正在运行一个旧版本的WTForms,对我而言,在1.0.1上它可以工作。


3
请注意,对于SelectMultipleField,默认值必须是一个列表,即:default=[2] - grandchild

20

有几种方法可以实现这个目的。你的第一段代码片段确实是正确的。

如果你想在视图中动态地实现这个目的,也可以这样做:

form = TestForm()
form.test_field.default = some_default_id
form.process()

非常感谢,我甚至在文档中都找不到解决方案,真的非常感激。 - Georges D

13

当您使用int时,这是带有SelectFieldchoices设置的工作方式:

test_select = SelectField("Test", coerce=int, choices=[(0, "test0"), (1, "test1")], default=1)
或者:
class TestForm(Form):
    test_select = SelectField("Test", coerce=int)

@app.route("/test")
def view_function():
    form = TestForm()
    form.test_select.choices = [(0, "test0"), (1, "test1")]
    form.test_select.default = 1
    form.process()

1
你测试过第二个变量了吗?在我的系统上调用 form.test_select.default = 1 没有改变任何东西。 - Robert
在使用整数ID时,设置coerce=int是一个重要的选项。我在两个表单上都设置了默认值,其中一个正常工作,而另一个不行。原来一个ID是字符串,另一个是整数。使用coerce选项解决了这个问题。 - rgacote
第二个工作正常,但有些东西需要您编辑。正确的语法是 form.test_select.process([])。process方法需要一个迭代参数。我测试的版本是2.3.1。 - crbroflovski

3

如果您正在使用flask_wtf,并且想要为FieldList中的不同SelectFields设置默认值,可以按照以下方式操作:

from flask_wtf import FlaskForm


class FormWithSelect(FlaskForm):
    my_select_field = SelectField(validators=[DataRequired()], coerce=int)


class FormWithMultipleSelects(FlaskForm):
    select_fields = FieldList(FormField(FormWithSelect), min_entries=1)
    submit = SubmitField(label="save all the select field values")

解决方案是覆盖 data 而不是像您所期望的那样覆盖 default
def generate_my_form(my_data, overwrite_data=True):
    form = FormWithMultipleSelects(select_fields=[{
        "my_select_field": {
             "name": d["name"],
             # ...
        }
    } for d in my_data]))
    for n range(len(form.select_fields)):
        form.select_fields[n].my_select_field.choices = [(1,'foo'), (2,'bar')]
        # I wan't 'bar' to be the option selected by default
        # form.select_fields[n].my_select_field.default = 2  # has no effect!
        if overwrite_data:
            form.select_fields[n].my_select_field.data = 2 #  works

基本上,这个解决方案与上面Elliots answer相似;我只是想为FieldList更复杂的情况提供一个解决方案。nikitz在评论中提到一个副作用:如果您覆盖data,那么form.validate_on_submit()将无法工作!您可以构建一个简单的解决方法,在调用validate_on_submit()时禁用覆盖data
 form_for_validation = generate_my_form(my_data, False)
 if form_for_validation.validate_on_submit():
      # do something with form_for_validation.data
 else:
     form_with_default_data = generate_my_form(my_data, True)
     render_template(..., form=form_with_default_data)

这不是非常优雅,但它能够正常工作。

附注:我的声望太低,无法直接评论Elliot的正确答案。


1
这篇文章的三分之二看起来更像是一个问题,而不是一个答案。如果你认为有帮助的话,可以先写出“解决方案是…”后面的部分,然后再提供背景信息。 - Roland Weber

1
Python Flask SelectField choices 

    # method 1 - routes.py
     form.proj_cat_id.default    = table1.proj_cat_id
     form.process()  # for preset 

    # method 2 - routes.py
    form.proj_cat_id.process_formdata([table1.proj_cat_id])



# method 3  - routes.py
      proj_cat_id_default    = table1.proj_cat_id
      return render_template('abc.html',proj_cat_id_default=proj_cat_id_default)

# method 3 - abc.html
        {% set z = form.proj_cat_id.process_data(proj_cat_id_default) %}
        {{ form.proj_cat_id(class="form-control form-control-lg") }}

0

我有一些关于动态设置默认选择字段的问题,我认为这可能很有用。模型是这样的:

class State(db.Model):
    __tablename__ = 'states'
    id = db.Column(db.Integer, primary_key=True)   
    name = db.Column(db.String(128), nullable=False)


class City(db.Model):
    __tablename__ = 'cities'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64))
    state_id = db.Column(db.Integer, db.ForeignKey('states.id'))   
    state = db.relationship(State, backref='state')


class User(UserMixin, db.Model):
    ...
    city_id = db.Column(db.Integer, db.ForeignKey('cities.id'))
    city = db.relationship('City', backref='city')

表单长这样:

class UserEditForm(FlaskForm):
    ...
    state = SelectField("state", coerce=int)
    city = SelectField("city", coerce=int)
    ....

  def __init__(self, state_id, *args, **kwargs):
       super(UserEditForm, self).__init__(*args, **kwargs)
       self.state.choices =  [(state.id, state.name)
                        for state in State.query.order_by('name').all()]
       self.city.choices = [(city.id, city.name)
                            for city in   City.query.filter_by(state_id=state_id).order_by('name').all()]   

而且视图是这样的:

@dashboard.route('/useredit', methods=['GET', 'POST'])
@login_required
def useredit():
    ....
    user = current_user._get_current_object()       
    form = OrganEditForm(state_id=user.city.state_id, state=user.city.state_id, city=user.city_id)
     ....

它运行良好并正确设置了Selectfield的默认值。但是我将代码更改为:

在视图中:

@dashboard.route('/useredit', methods=['GET', 'POST'])
    @login_required
    def useredit():
        ....
        user = current_user._get_current_object()       
        form = OrganEditForm(obj=user)
         ....

在表单中

def __init__(self, *args, **kwargs):
       super(UserEditForm, self).__init__(*args, **kwargs)
       self.state.choices =  [(state.id, state.name)
                        for state in State.query.order_by('name').all()]
       self.city.choices = [(city.id, city.name)
                            for city in   City.query.filter_by(state_id=kwargs['obj'].city.state_id).order_by('name').all()] 

但是这次城市默认值没有设置。我把表单改成了这样:

def __init__(self, *args, **kwargs):
           kwargs['city'] = kwargs['obj'].city_id
           super(UserEditForm, self).__init__(*args, **kwargs)
           self.state.choices =  [(state.id, state.name)
                            for state in State.query.order_by('name').all()]
           self.city.choices = [(city.id, city.name)
                                for city in   City.query.filter_by(state_id=kwargs['obj'].city.state_id).order_by('name').all()] 

但它没有起作用。我尝试了许多解决方案,最后我将城市变量名更改为usercity:

usercity= SelectField("city", coerce=int)

并将 kwargs['city'] = kwargs['obj'].city_id 修改为 kwargs['usercity'] = kwargs['obj'].city_id,之后它就正常工作了。

问题出在当我设置obj=user时,城市选择字段的默认值从kwargs['obj'].city读取,而这个属性是我在user模型中定义的。


0

我遇到了同样的问题。在我的情况下,Selectfields 没有在我的 HTML 上更新。Stringfields 和 Datefields 没有受到影响。

{{ wtf.form_field(form.my_field, class="form-control", values=some_values_from_sql[8]) }}

我的render_template如下:

render_template('my.html', form=form, some_values_from_sql=[x for x in sql_data[0]])

将代码更改为:

new_choice = [x for x in sql_data[0]]
form.my_field.default = new_choice[8]
form.my_field.process([])

还有我的 HTML 文件:

{{ wtf.form_field(form.my_field, class="form-control") }}

现在一切都正常工作了


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接