I have a WTForms field (value_currency) that I want to sometimes be a SelectField and sometimes a HiddenField. I use the same view and template for a page that both creates new items and edits existing items. If I load the page to create a new item, I want this field to be a SelectField, and if I load the page to edit an existing item, I want this field to be a HiddenField because it's a non-editable field.
Here is what I have so far:
FORM
class PromoForm(Form):
value = StringField('value')
currencies = Currency.query.order_by(Currency.id).all()
currency_choices = []
for currency in currencies:
currency_choice = (currency.id, currency.name)
currency_choices.append(currency_choice)
value_currency = SelectField('value_currency', choices=currency_choices)
VIEW
@app.route('/promo/<id>', methods=['GET', 'POST'])
@login_required
def promo(id):
form = PromoForm()
# Existing promo will pass in its id
# id = 0 if a new promo is to be created
if id != str(0):
# Load existing promo
promo = Promo.query.get(id)
# display value in decimal format
form.value.default = "{0}.{1:0>2}".format(
promo.value_cents//100, promo.value_cents%100)
form.process()
return render_template('promo.html', promo=promo, form=form)
else:
# New promo
audit_log('GET', client, session=session)
return render_template('promo.html', form=form)
TEMPLATE
{% extends "base.html" %}
{% block content %}
{% if promo is defined %}
<form action="{{ url_for('.promo', id=promo.id) }}" method="post">
{% else %}
<form action="{{ url_for('.promo', id=0) }}" method="post">
{% endif %}
{{ form.hidden_tag() }}
<div>
<label for="value">Promo Value</label>
{% if promo is defined %}
{{ form.value() }}
{% else %}
{{ form.value() }}
{% endif %}
{% for error in form.value.errors %}
<span class="error">[{{ error }}]</span>
{% endfor %}
{% if promo is defined %}
# ----> Promo.value_currency should be a hidden field here (Doesn't work)
{{ promo.value_currency }}
{% else %}
# ----> Promo.value_currency is a select field here (Currently works)
{{ form.value_currency() }}
{% endif %}
</div>
<div class="submit_btn">
{% if promo is defined %}
<input type="submit" value="Update Promo">
{% else %}
<input type="submit" value="Create Promo">
{% endif %}
</div>
{% endblock %}
I know I can just simply hardcode the hidden input element and drop in the value with Jinja, but I prefer to do it with WTForms and not do any form element hard coding. Is that possible?
See (duplicated) question: Flask, WTForms: Is there a way to make a StringField in the form _temporarily_ hidden?.
You cannot just omit the field, and you cannot change its object type (from SelectField to HiddenField) either.
However, you can change its widget object dynamically.
Replace this with a HiddenInput
.
from wtforms.widgets import HiddenInput
class PromoForm(Form):
value = StringField('value')
currencies = Currency.query.order_by(Currency.id).all()
currency_choices = []
for currency in currencies:
currency_choice = (currency.id, currency.name)
currency_choices.append(currency_choice)
value_currency = SelectField('value_currency', choices=currency_choices)
def hide_value_currency(self, value):
"""
Hide the value_currency field by morping it into a
HiddenInput.
"""
self.value_currency.widget = HiddenInput()
# wtforms chokes if the data attribute is not present
self.value_currency.data = value
# wtforms chokes on SelectField with HiddenInput widget
# if there is no _data() callable
self.value_currency._value = lambda: value
Call form.hide_value_currency(pre_set_value)
in your view when required.
No logic in your template necessary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With