Как использовать WTForms FieldList из FormFields?
Я создаю сайт, используя Flask, в котором я использую WTForms. В форме я теперь хочу использовать FieldList FormFields следующим образом:
class LocationForm(Form):
location_id = StringField('location_id')
city = StringField('city')
class CompanyForm(Form):
company_name = StringField('company_name')
locations = FieldList(FormField(LocationForm))
поэтому, чтобы дать людям возможность войти в компанию с двумя местоположениями (динамическое добавление местоположений происходит позже), я делаю это на лицевой стороне:
<form action="" method="post" role="form">
{{ companyForm.hidden_tag() }}
{{ companyForm.company_name() }}
{{ locationForm.location_id() }}
{{ locationForm.city() }}
{{ locationForm.location_id() }}
{{ locationForm.city() }}
<input type="submit" value="Submit!" />
</form>
Поэтому при отправке я печатаю места:
print companyForm.locations.data
но я получаю
[{'location_id': u'', 'city': u''}]
Я могу напечатать значения первого местоположения, используя locationForm (см. Ниже), но я все еще не знаю, как получить данные второго местоположения.
print locationForm.location_id.data
print locationForm.city.data
Таким образом, список местоположений имеет один диктант с пустыми значениями, но:
- Почему в списке локаций только один, а не два слова?
- И почему значения в местоположении пусты?
Кто-нибудь знает, что я здесь делаю не так? Все советы приветствуются!
2 ответа
Для начала, есть аргумент для FieldList, который называется min_entries
, что освободит место для ваших данных:
class CompanyForm(Form):
company_name = StringField('company_name')
locations = FieldList(FormField(LocationForm), min_entries=2)
Это позволит настроить список так, как вам нужно. Далее вы должны визуализировать поля непосредственно из locations
свойство, поэтому имена генерируются правильно:
<form action="" method="post" role="form">
{{ companyForm.hidden_tag() }}
{{ companyForm.company_name() }}
{{ companyForm.locations() }}
<input type="submit" value="Submit!" />
</form>
Посмотрите на визуализированный html, входные данные должны иметь такие имена, как locations-0-city
Таким образом, WTForms будет знать, что есть что.
В качестве альтернативы, для пользовательского рендеринга элементов сделайте
{% for l in companyForms.locations %}
{{ l.form.city }}
{% endfor %}
(только в одной форме l.city
это сокращение для l.form.city
, Тем не менее, этот синтаксис, кажется, конфликтует с Jinja, и там необходимо использовать явный l.form.city
в шаблоне.)
Теперь, чтобы подготовить представленные данные, просто создайте CompanyForm
и итерации по местам:
for entry in form.locations.entries:
print entry.data['location_id']
print entry.data['city']
Это старый вопрос, но все еще хороший.
Я хотел бы добавить рабочий пример игрушечной базы данных на основе Flask (просто список строк) с акцентом на часть Python - как инициализировать форму с переменным количеством подчиненных форм и как обрабатывать размещенные данные.
Это
example.py
файл:
import flask
import wtforms
import flask_wtf
app = flask.Flask(__name__)
app.secret_key = 'fixme!'
# not subclassing from flask_wtf.FlaskForm
# in order to avoid CSRF on subforms
class EntryForm(wtforms.Form):
city = wtforms.fields.StringField('city name:')
delete = wtforms.fields.BooleanField('delete?')
class MainForm(flask_wtf.FlaskForm):
entries = wtforms.fields.FieldList(wtforms.fields.FormField(EntryForm))
submit = wtforms.fields.SubmitField('SUBMIT')
city_db = "Graz Poprad Brno Basel Rosenheim Torino".split() # initial value
@app.route("/", methods=['POST'])
def demo_view_function_post():
global city_db
form = MainForm()
if form.validate_on_submit():
city_db = [
entry['city'] for entry in form.entries.data
if entry['city'] and not entry['delete']]
return flask.redirect(flask.url_for('demo_view_function_get'))
# handle the validation error, i.e. flash a warning
return flask.render_template('demo.html', form=form)
@app.route("/")
def demo_view_function_get():
form = MainForm()
entries_data = [{'city': city, 'delete': False} for city in city_db]
entries_data.append({'city': '', 'delete': False}) # "add new" placeholder
form.process(data={'entries': entries_data})
return flask.render_template('demo.html', form=form)
Это
demo.html
файл:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Demo</title>
</head>
<body>
<h1>Subform demo</h1>
<p>Edit names / mark for deletion / add new</p>
<form method="post">
{{ form.csrf_token() }}
{% for entry in form.entries %}
{% if loop.last %}
<div>Add new:</div>
{% endif %}
<div>
{{ entry.city.label }} {{ entry.city() }}
{{ entry.delete() }} {{ entry.delete.label }}
</div>
{% endfor %}
{{ form.submit() }}
</form>
</body>
Бежать с:
FLASK_APP=example flask run