Flask: Модальное окно bootstrap с валидацией формы

Писал я приложение на Flask, захотелось мне сделать модальное bootstrap окно с формой редактирования профиля пользователя, погуглил и нашел отличную статью, так сказать отправную точку в изучении вопроса. Но главная проблема была в том, что в статье описан пример модальной формы находящейся в том же роуте, где модальное окно и вызывается, а я хотел сделать доступной данную форму с любой страницы (образно говоря, разместить в шапке сайта кнопку «редактировать профиль» по которой откроется модальное окно для редактирования профиля (а на самом деле роут для этой формы /user/<id>/edit).

Сразу оговорюсь, данное решение применимо к приложению на Flask, wtforms, bootstrap.

Итак, модель и форма уже должны быть созданы. Показываю пример роута для этой формы.

import json
from flask import render_template, request, jsonify

@app.route('/user/<id>/edit', methods=['GET', 'POST'])
def user_edit(id):
    user = User.query.filter_by(id=id).first_or_404()
    form = ProfileEditForm(user.email)
    if form.validate_on_submit():
        user.email = form.email.data
        db.session.commit()
        return jsonify(status='ok')
    elif request.method == 'GET':
        form.email.data = user.email
    else:
        data = json.dumps(form.errors, ensure_ascii=False)
        return jsonify(data)
    return render_template('_form_edit.html', title="Редактирование пользователя", form=form)

Смысл таков.

  • Если GET — отдаем форму с данными (если они есть) используя шаблон формы _form_edit.html
  • Если POST (форма прошла валидацию) — сохраняем форму и отвечаем json’ом «ok»
  • Если форма не прошла валидацию — собираем ошибки валидации в json и отправляем в ответ.

Теперь переходим к «front-end». Для начала добавим html пустого модального окна (я это сделал сразу в base.html, это у меня header).

<!-- Dynamic Modal -->
<div class="modal fade" id="Modal" tabindex="-1" role="dialog" aria-labelledby="FormModal" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
         <!-- load content here -->
        </div>
    </div>
</div>
<!-- End Dynamic Modal -->

и сразу же кнопку, которая будет его вызывать. Добавляйте на любую страницу или сразу в base.html в главное меню.

<a class="edit-modal-opener" data-toggle="modal"
                            data-whatever="{{ url_for('user_edit', id=user.id) }}" href="#">Изменить</a>

А теперь шаблон содержимого модального окна (modal-content) — _form_edit.html

{% from 'bootstrap/wtf.html' import form_field %}
<form id="ModalForm" name="stepForm" class="form" method="post">
    <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="close">
            <span aria-hidden="true">&times;</span>
        </button>
        <h4 class="modal-title">{{ title }}</h4>
    </div>
    <div class="modal-body">

        {{ form.hidden_tag() }}
        {{ form_field(form.email) }}

    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
        <button id="submit" type="submit" class="btn btn-success">Сохранить</button>
    </div>
</form>

И самый сок — javascript, который собственно подгружает форму в модальное окно, ожидает ответ «ok» от бэка или json с ошибками, для того, что-бы присвоить эти ошибки валидации к полям формы. Это нужно поместить в блок <script> в base.html или отдельный файл, если вы реализовали это.

$(document).ready(function () {
    $('.edit-modal-opener').click(function () {
        var url = $(this).data('whatever');
        $.get(url, function (data) {
            $('#Modal .modal-content').html(data);
            $('#Modal').modal();

            $('#submit').click(function (event) {
                event.preventDefault();
                $.post(url, data = $('#ModalForm').serialize(), function (
                    data) {
                    if (data.status == 'ok') {
                        $('#Modal').modal('hide');
                        location.reload();
                    } else {
                        var obj = JSON.parse(data);
                        for (var key in obj) {
                            if (obj.hasOwnProperty(key)) {
                                var value = obj[key];
                            }
                        }
                        $('.help-block').remove()
                        $('<p class="help-block">' + value + '</p>')
                            .insertAfter('#' + key);
                        $('.form-group').addClass('has-error')
                    }
                })
            });
        })
    });
});

Собственно на этом все. Все запросы формы (GET и POST) будут передаваться в фоне, модальное окно закроется только после успешной валидации и сохранения формы, если flask-wtf вернет ошибки валидации — форма это отобразит без перезагрузки страницы.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *