¿Cómo establece un valor predeterminado para un WTForms SelectField?

Al intentar establecer el valor predeterminado de un SelectField con WTForms, paso el valor al parámetro ‘predeterminado’ de esta manera.

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

También he intentado lo siguiente.

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

Ninguno establece el campo seleccionado por defecto en “Def.” Esto funciona para otros tipos de campos como TextField. ¿Cómo establece el valor predeterminado para un SelectField?

La primera forma en que publicaste es correcta, y funciona para mí. La única explicación para que no funcione puede ser que está ejecutando una versión anterior de WTForms, funcionó para mí en 1.0.1

Creo que este problema está causado por el atributo de data del Field reemplaza el default con algo que WTForms no comprende (por ejemplo, un objeto de modelo DB – espera un int ). Esto sucedería si ha rellenado su formulario en el constructor así:

 form = PostForm(obj=post) 

la solución es configurar manualmente el atributo de data después de que se haya completado el formulario:

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

Hay algunas maneras de hacer esto. Su primer fragmento de código es de hecho correcto.

Si quieres hacer esto en una Vista dinámicamente, también puedes hacer:

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

Flask-WTF 0.14.2 usuario aquí. Así que esta respuesta es para cualquiera que tenga un problema similar conmigo.

Básicamente, ninguna de las soluciones anteriores funciona correctamente con form.validate_on_submit() .

La configuración de form.test_field.data cambiará el valor predeterminado cuando se cargue la página, pero los datos permanecen iguales después de validate_on_submit (los cambios realizados por el usuario en el navegador no tienen efecto).

La configuración de form.test_field.default luego llamar a form.process() también cambia el valor cuando se carga la página, pero validate_on_submit fallará.

Aquí está la nueva forma de hacerlo:

 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(): ... 

Esta es una configuración de choices con SelectField cuando usas un int , funciona así:

 test_select = SelectField("Test", coerce=int, choices=[(0, "test0"), (1, "test1")], default=1) 

o:

 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() 

En caso de que esté utilizando flask_wtf y desee establecer un valor predeterminado para diferentes SelectFields nesteds en una FieldList como esta

 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") 

La solución es sobrescribir los data , no los valores default como cabría esperar.

 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 

Básicamente esta solución es similar a la respuesta de Elliots anterior; Solo quería proporcionar una solución para el caso más complejo de una FieldList. nikitz mencionó en un comentario que hay un efecto secundario: form.validate_on_submit() no funciona si está sobrescribiendo data . Puede crear una solución sencilla donde deshabilitar la sobrescritura de data al llamar a 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) 

No es super elegante, pero funciona.

PD: Mi reputación es demasiado baja para comentar directamente la respuesta correcta de Elliot.

Tengo algún problema con el campo de selección predeterminado del conjunto dynamic que creo que puede ser útil. El modelo era así:

 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') 

La forma era así:

 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()] 

y la vista era así:

 @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) .... 

funciona bien y establece el valor predeterminado de Selectfield correctamente. Pero cambié el código así:

en vista:

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

en formas

 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()] 

pero esta vez el valor predeterminado de la ciudad no se establece. Cambié de forma de esta manera:

 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()] 

pero no funcionó. Probé muchas soluciones y en la última cambié el nombre de la variable de ciudad a usercity:

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

y kwargs['city'] = kwargs['obj'].city_id to kwargs['usercity'] = kwargs['obj'].city_id . Afirmó que funcionó correctamente.

el problema era que cuando establecía obj=user , el valor predeterminado para el campo de selección de ciudad leía desde kwargs['obj'].city que kwargs['obj'].city en user modelo de user .