У меня есть приложение flask + wtforms, в котором я хочу, чтобы пользователи могли вводить как родительский объект, так и произвольное количество дочерних объектов. Я не уверен, как лучше всего динамически создавать новые поля ввода дочерней формы из пользовательского интерфейса.
Что у меня есть до сих пор
Ниже приведен полный рабочий пример. (Примечание: это искусственный пример выделения всех рабочих частей в одном файле .py, что делает код довольно беспорядочным. Извините.)
from flask import Flask, render_template_string
from flask_wtf import FlaskForm
from wtforms import FieldList, FormField, StringField, SubmitField
from wtforms.validators import InputRequired
# Here I'm defining a parent form, AuthorForm, and a child form, BookForm.
# I'm using the FieldList and FormField features of WTForms to allow for multiple
# nested child forms (BookForms) to be attached to the parent (AuthorForm).
class BookForm(FlaskForm):
title = StringField('title', validators=[InputRequired()])
genre = StringField('genre', validators=[InputRequired()])
# I'm defining a min_entry of 1 so that new forms contain a blank BookForm entry
class AuthorForm(FlaskForm):
name = StringField('name', validators=[InputRequired()])
books = FieldList(FormField(BookForm), min_entries=1)
submit = SubmitField('Save')
# Here's my Jinja template
html_template_string = """
<html>
<head><title>stackoverflow example</title></head>
<body>
<form action="" method="post" role="form">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{% for book in form.books %}
<p>
{{ book.title.label }} {{ book.title() }}
{{ book.genre.label }} {{ book.genre() }}
{{ book.hidden_tag() }}
</p>
{% endfor %}
{{ form.submit() }}
</form>
</body>
</html>
"""
# Alright, let's start the app and try out the forms
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
@app.route('/', methods=['GET', 'POST'])
def index():
form = AuthorForm()
if form.validate_on_submit():
for book in form.books.data:
print(book)
return render_template_string(html_template_string, form=form)
if __name__ == '__main__':
app.run()
Где я застрял
Я знаю, как создавать новые дочерние записи (записи BookForm) на стороне сервера. Я мог бы просто передать пустые словари в свою форму, и WTForms сгенерировал бы для меня входные данные:
...
form = AuthorForm()
form.books.append_child({})
form.books.append_child({})
form.books.append_child({})
...
Бам, теперь на моей странице есть поля ввода для трех книг, и я мог бы предварительно заполнить их данные. WTForms генерирует содержимое ввода формы перед отображением страницы. Он обрабатывает все идентификаторы, необходимые для каждого входа и т. д.
Если я хочу, чтобы пользователь мог щелкнуть кнопку и добавить новые экземпляры ввода BookForm со стороны пользователя после того, как страница была отрисована… как мне это сделать? Должен ли я сам вручную создавать поля ввода в JavaScript, используя в качестве эталона то, что было сгенерировано WTForms? Это кажется грязным и склонным к поломке или, по крайней мере, к уродливому коду.
Есть ли способ для WTForms отображать HTML для новых входных данных по мере необходимости, управляя уникальностью идентификаторов входных тегов и т. Д.? Я мог бы отправить что-то обратно на сервер, чтобы добавить пустые записи в форму и повторно отобразить страницу, но это лишило бы меня всего моего существующего пользовательского ввода, поэтому на самом деле это не работает. Этот вопрос Stackoverflow предполагает то же самое в комментариях, и то же самое возражение поднятый.
Если это должно быть сделано уродливым ручным способом, то я могу это сделать. Я просто не хочу использовать плохой подход, когда есть лучшее (и, возможно, даже официальное) решение этой проблемы.
Это в основном то, для чего был создан jquery. — person daveruinseverything schedule 29.04.2019
Этот ответ делает что-то вроде того, что требуется. Клонирование соответствующих полей в js, увеличение идентификаторов и рендеринг «новой» разметки — единственный разумный способ сделать это, я думаю. — person daveruinseverything schedule 29.04.2019
Я ПРЕКРАСНО понимаю, в чем ваш вопрос, чтобы использовать чистый Flask для достижения этой цели. Но я был в такой же ситуации пару месяцев назад. И после многих исследований и странных и избыточных методов я обнаружил, что реализация этого будет не только кропотливой, но долгой и трудной, и любое небольшое изменение или усилия по отладке просто сломают все это.
Я знаю, что это не совсем то, о чем вы просили. Но если вы хотите сделать это с помощью Flask+ jQuery. Я покажу вам часть работы, которую я проделал для динамической загрузки данных из интерфейса HTML через представления, надеюсь, что это может быть вам полезно, ЕСЛИ вы передумаете и решите включить jQuery.
Это моя форма, использующая HTML и Jinja2:
Это представление, которое вызывает эту форму:
И это небольшой полезный скрипт jQuery, который я написал, который вы можете включить в HTML с помощью тега
<script>
.Используя jQuery и метод
getJSON()
, я могу отправить запрос в это представление:Таким образом, каждый раз, когда происходит изменение, данные динамически считываются с сайта, отправляются в представление Flask, обрабатываются там тем, что вы хотите, отправляются обратно тем же самым
getJSON()
и вставляются прямо в форму!! Вуаля, вы динамически использовали jQuery+Flask для манипулирования тем, что хотите сделать, используя своего нового лучшего друга JSON.После долгих исследований я нашел самый простой способ сделать то, что вы хотите, через jQuery. Изучите методы
getJSON()
иpost()
в jQuery, включите их в свой HTML, и вы получите ответ. А такжеjsonify()
, который, как мне кажется, можно импортировать в Python черезimport json
.Передайте список в
jsonify()
, который вы можете использовать для возврата в качестве представления в том жеgetJSON()
, который вы использовали для отправки данных.Я приму это как лучший ответ — @dustin-ingram, если вы согласны, вы можете присудить награду — person daveruinseverything; 02.05.2019