【问题标题】:Flask - Form on Modal - How to display the field error on modal?Flask - 模态表单 - 如何在模态上显示字段错误?
【发布时间】:2020-01-20 18:41:37
【问题描述】:

我正在尝试使用烧瓶并只是创建一些基本功能来将数据添加到以模式显示的表单中。虽然我设法让表单在模态中显示并从模态中保存,但我很难理解需要做什么来确保字段验证错误显示在模态本身上。目前,如果出现错误,用户将被重定向到带有编辑表单的整个页面。

他们说图片胜于文字 - 所以这里有一个 gif 展示正在发生的事情:

整个应用程序代码在github 上,heroku 上的当前状态可以访问here... 用户名:admin@admin.com 密码:adminpassword时间>。反正都是沙盒。

相关代码如下:

routes.py


@expenses.route("/expense")
@login_required
def expense():
    page = request.args.get('page', 1, type=int)
    expenses = Expense.query.order_by(Expense.expense_date.desc()).paginate(page=page, per_page=5)
    form = ExpenseForm()
    return render_template('expense/expense.html', expenses=expenses, form=form)


@expenses.route("/expense/new", methods=['GET', 'POST'])
@login_required
def new_expense():
    form = ExpenseForm()
    if form.validate_on_submit():
        expense = Expense(description=form.description.data, expense_date=form.expense_date.data,
                        amount=form.amount.data,vat_amount=form.vat_amount.data,Transferrable=form.Transferrable.data, author=current_user)
        db.session.add(expense)
        db.session.commit()
        flash('Your expense has been created!', 'success')
        return redirect(url_for('expenses.expense'))
    return render_template('expense/create_expense.html', title='New Expense',
                           form=form, legend='New Expense')

现在费用.html 很大,但在其上使用以下方式调用模态:

<button type="button" class="btn btn-primary btn-sm m-1" data-toggle="modal" data-target="#AddNewModal">Add New Expense</button>
{% include "expense/partials/addModal.html" %}

addModal.html如下图:

<!-- Add New Modal -->
{% from "util/macros.html" import form_field with context %}
<div class="modal fade" id="AddNewModal" tabindex="-1" role="dialog" aria-labelledby="AddNewModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="AddNewModalLabel">Add New Expense</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
        <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <div class="amount-section">
          <form method="POST" action="/expense/new">
          {{ form.hidden_tag() }}
          <fieldset class="form-group">
            <legend class="border-bottom mb-4">{{ legend }}</legend>
            <div class="form-group">
              {{ form.description.label(class="form-control-label") }}
              {% if form.description.errors %}
                {{ form.description(class="form-control form-control-lg is-invalid") }}
                <div class="invalid-feedback">
                  {% for error in form.description.errors %}
                  <span>{{ error }}</span>
                  {% endfor %}
                </div>
              {% else %}
                {{ form.description(class="form-control form-control-lg") }}
              {% endif %}
            </div>
            <div class="form-group">
              {{ form.amount.label(class="form-control-label") }}
              {% if form.amount.errors %}
                {{ form.amount(class="form-control form-control-lg is-invalid") }}
                <div class="invalid-feedback">
                {% for error in form.amount.errors %}
                  <span>{{ error }}</span>
                {% endfor %}
                </div>
              {% else %}
                {{ form.amount(class="form-control form-control-lg") }}
              {% endif %}
            </div>
            <div class="form-group">
              {{ form.expense_date.label(class="form-control-label") }}
              {% if form.expense_date.errors %}
                {{ form.expense_date(class="form-control form-control-lg is-invalid") }}
                <div class="invalid-feedback">
                  {% for error in form.expense_date.errors %}
                    <span>{{ error }}</span>
                  {% endfor %}
                </div>
              {% else %}
                {{ form.expense_date(class="form-control form-control-lg", type="date") }}
              {% endif %}
            </div>
            <div class="form-group">
              {{ form.vat_amount.label(class="form-control-label") }}
              {% if form.vat_amount.errors %}
                {{ form.vat_amount(class="form-control form-control-lg is-invalid") }}
                <div class="invalid-feedback">
                  {% for error in form.vat_amount.errors %}
                    <span>{{ error }}</span>
                  {% endfor %}
                </div>
              {% else %}
                {{ form.vat_amount(class="form-control form-control-lg") }}
              {% endif %}
            </div>
            <!-- {{ form_field(form.vat_amount,with_label=True) }} -->
            <div class="form-group">
            {% if form.Transferrable.errors %}
              {{ form.Transferrable(class="form-control form-control-lg is-invalid") }}
              <div class="invalid-feedback">
                {% for error in form.Transferrable.errors %}
                  <span>{{ error }}</span>
                {% endfor %}
              </div>
            {% else %}
              {{ form.Transferrable(type="checkbox") }}
            {% endif %}
              {{ form.Transferrable.label(class="form-control-label") }}
            </div>
            <!-- {{ form_field(form.Transferrable) }} -->
            <p><button type="submit" class="btn btn-primary">Add</button></p>
            </fieldset>
          </form>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        </div>
      </div>
    </div>
  </div>
</div>

【问题讨论】:

  • 我希望所有问题都如此清晰和描述性;)
  • 谢谢 - 你真是太好了。 :)

标签: flask flask-sqlalchemy flask-wtforms flask-bootstrap


【解决方案1】:

这是因为如果表单有一些错误,你会渲染create_expense.html,这就是表单不以模态显示的原因。

我将合并 2 个视图:/expense/new/expense,以便它可以同时处理 GETPOST,并且如果表单有一些错误,则有条件地显示模式。

合并视图:

@expenses.route("/expense", methods=['GET', 'POST'])
@login_required
def expense():
    page = request.args.get('page', 1, type=int)
    expenses = Expense.query.order_by(Expense.expense_date.desc()).paginate(page=page, per_page=5)
    form = ExpenseForm()
    if form.validate_on_submit():
        expense = Expense(description=form.description.data, expense_date=form.expense_date.data,
                        amount=form.amount.data,vat_amount=form.vat_amount.data,Transferrable=form.Transferrable.data, author=current_user)
        db.session.add(expense)
        db.session.commit()
        flash('Your expense has been created!', 'success')
    return render_template('expense/expense.html', expenses=expenses, form=form)

addModal.html底部显示的条件模态:

{% if form.errors %}

<script>
$('#AddNewModal').modal('show');
</script>

{% endif %}

addModal.html 中表单的操作也必须更改为:

<form method="POST" action="/expense">

但是,在这些更改之后,视图 /expense/new/expense 将有一些共同的代码,因此可能需要重构。现在你至少知道为什么错误没有显示在模态中了。

【讨论】:

  • 谢谢.. 它帮助我了解了发生了什么,但我现在对表单验证的一种行为感到困惑 - 查看 DataRequired 验证触发器,无需刷新但数据类型的完整性(整数与字符串)仅在往返数据库后触发。这是为什么? - 我认为从后端获取错误而不是在前端处理它们是不好的做法,我是否不正确。如果是这样,我将如何在这里实现?
  • @techbolt 验证在后端执行,导致页面快速重新加载。您可以看到您的浏览器在开发者工具 -> 网络中对模态表单提交发出 POST 请求。要获得额外的验证层,您可以修改表单字段,以便引导程序在实际调用后端之前处理错误。
  • 非常感谢您抽出时间来帮助我......这更有意义。我将字段的类型更改为数字,现在它不允许我输入字符串,所以这肯定是一种更好的方法。基本上我将 addModal 上的字段更改为 {{ form.amount(class="form-control form-control-lg", value=expense.amount, type="number", required="required", step="0.01") }}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-05
  • 2017-06-12
  • 1970-01-01
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
相关资源
最近更新 更多