Как использовать 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

Таким образом, список местоположений имеет один диктант с пустыми значениями, но:

  1. Почему в списке локаций только один, а не два слова?
  2. И почему значения в местоположении пусты?

Кто-нибудь знает, что я здесь делаю не так? Все советы приветствуются!

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

Другие вопросы по тегам