Reputation: 2433
When attempting to set the default value of a SelectField with WTForms, I pass in value to the 'default' parameter like so.
class TestForm(Form):
test_field = SelectField("Test: ", choices=[(1, "Abc"), (2, "Def")], default=2)
I have also tried the following.
class TestForm(Form):
test_field = SelectField("Test: ", choices=[(1, "Abc"), (2, "Def")], default=(2, "Def"))
Neither set the default selected field to "Def." This works for other kinds of Fields such as TextField. How do you set the default value for a SelectField?'
Upvotes: 72
Views: 95189
Reputation: 1
For people coming here in 2024, in the WTForm documentation:
Also, the coerce keyword arg to SelectField says that we use int() to coerce form data. The default coerce is str().
By default the value to check on for the default/preselected, is a string. Unless you have created your SelectField to include the option "coerce=int
", what you want to match is "2", so you need default="2"
for it to display the preselected option.
so for example for:
rating = SelectField(label='Your Rating', choices=[0,1,2,5,8,10])
then you can set the default in your form
form.rating.data = "2"
otherwise
with coerce to convert
rating = SelectField(label='Your Rating', choices=[0,1,2,5,8,10], coerce=int)
form.rating.data = 2
Upvotes: 0
Reputation: 1
I had the same issue. In my case the Selectfields didn't update on my html. Stringfields and Datefields weren't affected.
{{ wtf.form_field(form.my_field, class="form-control", values=some_values_from_sql[8]) }}
My render_template is like:
render_template('my.html', form=form, some_values_from_sql=[x for x in sql_data[0]])
After changing the code to:
new_choice = [x for x in sql_data[0]]
form.my_field.default = new_choice[8]
form.my_field.process([])
And my html file:
{{ wtf.form_field(form.my_field, class="form-control") }}
Everything works fine now
Upvotes: 0
Reputation: 21
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") }}
Upvotes: 1
Reputation: 1405
Flask-WTF 0.14.2
user here. So this answer is for anyone who have similar problem with me.
Basically, none of the previous solutions function properly with form.validate_on_submit()
.
Setting form.test_field.data
will indeed change the default value when the page is loaded, but the data stays the same after validate_on_submit
(user changes in browser has no effect).
Setting form.test_field.default
then call form.process()
also changes the value when the page is loaded, but validate_on_submit
will fail.
Here is the new way to do it:
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():
...
Upvotes: 48
Reputation: 1385
I have some problem with dynamic set default selectfield thati think can be useful. the model was like this:
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')
the form was like this:
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()]
and the view was like this:
@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)
....
it works good and set Selectfield default value correctly. but i changed the code like this:
in view:
@dashboard.route('/useredit', methods=['GET', 'POST'])
@login_required
def useredit():
....
user = current_user._get_current_object()
form = OrganEditForm(obj=user)
....
in forms
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()]
but this time the city default value doesn't set. i changed form like this:
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()]
but it didn't work. I tried many solution and in the last I changed the city variable name to usercity:
usercity= SelectField("city", coerce=int)
and kwargs['city'] = kwargs['obj'].city_id
to kwargs['usercity'] = kwargs['obj'].city_id
. affter that it worked correctly.
the problem was that when I set obj=user
, default value for city selectfield read from kwargs['obj'].city
that I defined in user
model.
Upvotes: 0
Reputation: 1849
In case you're using flask_wtf and would like to set a default value for different SelectFields nested in a FieldList like this
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")
the solution is to overwrite data
, not default
as you would expect.
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
Basically this solution is similar to Elliots answer above; I just wanted to provide a solution for the more complex case of a FieldList. nikitz mentioned in a comment, that there is a side effect: form.validate_on_submit()
does not work if you're overwriting data
! You can build a simple workaround where to disable the overwriting of data
when calling validate_on_submit()
.
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)
It's not super elegant, but it works.
PS: My reputation is too low to directly comment on the correct answer by Elliot.
Upvotes: 4
Reputation: 141
This is a choices
settings with SelectField
when you use an int
, it works like this:
test_select = SelectField("Test", coerce=int, choices=[(0, "test0"), (1, "test1")], default=1)
or:
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()
Upvotes: 13
Reputation: 8248
There are a few ways to do this. Your first code snippet is indeed correct.
If you want to do this in a View dynamically though, you can also do:
form = TestForm()
form.test_field.default = some_default_id
form.process()
Upvotes: 20
Reputation: 2619
I believe this problem is caused by the Field
's data
attribute overriding the default
with something that WTForms doesn't understand (e.g. a DB model object -- it expects an int
). This would happen if you have populated your form in the constructor like so:
form = PostForm(obj=post)
the solution is to manually set the data
attribute after the form has been populated:
form = PostForm(obj=post)
form.category.data = (post.category.id
if page.category
else 0) # I make 0 my default
Upvotes: 50
Reputation: 1326
The first way you posted is correct, and it works for me. The only explanation for it not working can be that you are running an older version of WTForms, it worked for me on 1.0.1
Upvotes: 41