Flask wtform + jquery 异步提交modal最佳实践

以前无刷很火,现在又回到http协议本身,html也到了第五个版本,用flask也有了好些个年头。

flask用的人很多,可惜很多企业并不开源,像豆瓣这样公开架构的还是很有限,开源的东西里不是什么都是开源的,比如很多最佳实践都是散落在很多博客中。有的最佳实践只是做到了一部分,一个完整的最佳实践到现在才自己比较满意。没办法,越是灵活的东西也就意味着无限的可能性,更别说flask这种牛逼的微框架了。

在开始前请确认你了解wtform并且已经会使用它。用过才知道它的强大和灵活。但是一直没有好好的研究出怎么无缝的使用它和ajax。主要需要实现如下的几个目标:

  1. 无刷,呵呵,必须的;
  2. view只有一个method,能够同时实现新建、编辑、保存,因为wtform就能很好的支持,所以做不到不算最佳;
  3. template只有一个,模板最烦,重复展现最恶心,所以如何最大化重用是必须要考虑的,能省下很多work hour;

大概的思路是先get加载新建的表单,ajax提交表单数据,返回status为ok则表明添加成功,否则返回html(这时的form已经包含了字段的错误信息)

下面是展示例的代码

<pre>
<form role="form" method="POST" class="form-horizontal {{ 'new-enterprise' if is_new }} enterprise-info" action="{{ url_for('enterprise_save_page',id=enterprise.id) }}" id="enterprise_form">

    
<div class="modal-header">
        
<h5 class="modal-title">{{ '添加企业' if is_new }}</h5>

        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
            <span aria-hidden="true">&times;</span>
        </button>
    </div>

    
<div class="modal-body">
        
<fieldset>
            {{ enterprise_form.hidden_tag() }}
            {{ render_field(enterprise_form.name) }}

            {{ render_select(enterprise_form.industry) }}
        </fieldset>

    </div>

    
<div class="modal-footer">
        <button type="submit" class="btn btn-modal-back" data-dismiss="modal" id="confirm_cancel">取消
        </button>
        <button type="button" class="btn btn-modal-confirm" data-dismiss="modal" id="confirm_ok">确认
        </button>
    </div>

</form>
</pre>
// add step button opens modal containing step form loaded via ajax
$('#btn_add_enterprise').click(function () {
    var url = "{{ url_for('enterprise_save_page',id=None) }}";
    $.get(url, function (data) {
        $('#enterprise_info_modal .modal-content').html(data);
        $('#enterprise_info_modal').modal({
            keyboard: false,
            show: true
        });

        $('#confirm_ok ').click(function (event) {
            event.preventDefault();
            $.post('{{ url_for('enterprise_save_page',id=0) }}', data = $('#enterprise_form').serialize(), function (data) {
                if (data.status == 'ok') {
                    $('#enterprise_info_modal').modal('hide');
                    alert('新企业已添加');
                    location.reload();
                }
                else {
                    $('#enterprise_info_modal .modal-content').html(data);
                    $('#enterprise_info_modal').modal({
                        keyboard: false,
                        show: true
                    });
                }
            });
        })
    });
});
@app.route('/admin/enterprise/new', methods=['GET'], defaults={'id': None})
@app.route('/admin/enterprise/save/<int:id>', methods=['POST', 'GET'])
@admin_required()
def enterprise_save_page(id):
    if id is not None and id > 0:
        enterprise = EnterpriseInfo.query.get(id)
    else:
        enterprise = EnterpriseInfo()
    enterprise_form = EnterpriseInfoForm(obj=enterprise)
    if enterprise_form.validate_on_submit():
        if id == 0:
            enterprise.id = None

            db.session.add(enterprise)
        db.session.commit()
        return jsonify(status='ok')

    args = locals()
    return render_template('admin/enterprise/_info.html', **args)