引导模态表单发布两次Django

2022-03-02 00:00:00 django html bootstrap-modal

我正在遵循django-bootstrap-modal-forms的说明,我发现在提交表单时,表单正在发布或提交两次。首先,该对象简单地创建了两次,我可以从管理员处看到这一点。现在,表单似乎正在创建对象,但也进入了它的验证状态,如果表单成功了,我显然不想要这样的状态,但它确实成功了。

有没有人经历过这种情况?除了我链接到的文档中概述的内容之外,我什么都没做,我找不出任何理由,为什么这应该提交两次。

以下是我的代码:

home.html

<a href="#" class="create-shipment dropdown-itemcreatenew" onclick="closeNav()">Shipment</a>

<div class="modal fade" tabindex="-1" role="dialog" id="modal">
  <div class="modal-dialog" role="document">
    <div class="modal-content">

    </div>
  </div>
</div>

<script>
$(document).ready(function() {

    $(".create-shipment").modalForm({
        formURL: "{% url 'CreateShipmentView' %}"
    });

});
</script>

views.py

class CreateShipmentView(BSModalCreateView):
    template_name = 'create_shipment.html'
    form_class = CreateShipmentForm
    success_message = 'Success: Shipment Has Been Created!'
    success_url = reverse_lazy('HomeView')

create_shiment.html(模式表单模板)

{% load crispy_forms_tags %}
<form method="post" action="">
  {% csrf_token %}

 <div class="modal-header">
    <h5 class="modal-title">Create New Shipment</h5>
    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>

  <div class="modal-body">
    {{form|crispy}}
  </div>

  <div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <button type="button" class="submit-btn btn btn-primary">Create</button>
  </div>

</form>

forms.py

class CreateShipmentForm(BSModalForm):
    class Meta:
        model = Shipment
        fields = ['Reference_Number', 'Ultimate_Consignee']

urls.py

url(r'^create_shipment/', views.CreateShipmentView.as_view(), name='CreateShipmentView'),

提交按钮上的事件侦听器

 // Add click listener to the submitBtn
    var ajaxSubmit = function (modalID, modalContent, modalForm, formURL, errorClass, submitBtn) {
        $(submitBtn).on("click", function () {
            // Check if form.is_valid() via ajax request when submitBtn is clicked
            isFormValid(modalID, modalContent, modalForm, formURL, errorClass, submitBtn, submitForm);
        });
    };

    // Check if form.is_valid() & either show errors or submit it
    var isFormValid = function (modalID, modalContent, modalForm, formURL, errorClass, submitBtn, callback) {
        $.ajax({
            type: $(modalForm).attr("method"),
            url: $(modalForm).attr("action"),
            // Serialize form data
            data: $(modalForm).serialize(),
            success: function (response) {
                if ($(response).find(errorClass).length > 0) {
                    // Form is not valid, update it with errors
                    $(modalID).find(modalContent).html(response);
                    $(modalForm).attr("action", formURL);
                    // Reinstantiate click listener on submitBtn
                    ajaxSubmit(modalID, modalContent, modalForm, formURL, errorClass, submitBtn);
                } else {
                    // Form is valid, submit it
                    callback(modalForm);
                }
            }
        });
    };

解决方案

阅读套餐source code后,相信后台收到两个请求是正常的。

您的代码

$(document).ready(function() {

    $(".create-shipment").modalForm({
        formURL: "{% url 'CreateShipmentView' %}"
    });

});

触发函数modalForm以获取您的选项(formURL),并将函数newForm分配给单击事件。

然后在newForm函数中调用函数addListeners将点击事件绑定到模态中的提交按钮,事件调用如下:

isFormValid(modalID, modalContent, modalForm, formURL, errorClass, submitBtn, submitForm);

请注意,最后一个参数submitForm指向以下函数

var submitForm = function(modalForm) {
      $(modalForm).submit();
    };

最后,在函数isFormValid中,您在表单中输入的所有数据都将发布到您在action属性中定义的url进行验证,如果没有错误,表单将提交到完全相同的url。

如果您挖洞进入这个包中的Python代码,事情就会变得有趣起来。BSModalForm基于mixins.py和here中的两个类,说明当请求不是由Ajax发出时,保存使用表单数据创建的实例,否则(如果Ajax调用请求)不保存该实例并返回它。这就是为什么它首先验证表单,但不应该保存它(请记住,第一个调用确实是通过使用jQuery中的Ajax调用发起的)。

您提到该表单正被保存两次-请尝试在save函数的开头添加一行,如下所示

print(request.is_ajax())

,然后检查输出。可能是调用未能作为Ajax调用发送。(如果是这种情况,请更新您的jQuery版本或使用其他方式(如axios)进行调用)

如果您不喜欢事情发生的方式,可以选择以下几个选项(麻省理工学院许可下的软件包):

  1. 更改save函数以验证实例,然后将其另存为普通的Django保存函数,但这涉及更改某些JS代码。

  2. 让API端点使用json接收数据并进行通信,而不是每次都返回html代码(我猜这也是作者以当前方式编写JS的原因,因为这样会面临呈现问题)。因为目前提交表单后不需要执行任何其他操作,所以再返回实例就没有意义了。(不需要DRF,因为Django中内置了JsonResponse类,并且如果您只需要这一个端点)

  3. 直接使用bootstrap,因为这里的故事相当简单:页面上有一个模式,触发模式的按钮,模式中的表单,您可以提交它。您可能需要编写一些自己的JS来显示错误,但这仍然比更改现有包容易。


示例

# forms.py
from django import forms


# use django ModelForm for creating the form based on model
class CreateShipmentForm(forms.ModelForm):
    class Meta:
        model = Shipment
        fields = ['Reference_Number', 'Ultimate_Consignee']

用于呈现表单(GET)和接收表单提交(POST)的视图

# views.py
from yourapp.forms import CreateShipmentForm
from django.shortcuts import render


def create_shipment_view(request):
    # http POST request means that the form was submitted
    if request.method == 'POST':
        # create the form and map the data to CreateShipmentForm
        shipment_form = CreateShipmentForm(request.POST)
        # based on your design of the form/modal, validate the data passed into the form
        if shipment_form.is_valid():
            # create a new shipment object
            new_shipment = shipment_form.save()
            # form processing is finished now do something to alert user, for example redirect the user to the success page
            return redirect('success')
        # if the form is not valid, then shipment_form.errors contain all the errors
    else:
        # for all other accessing methods including GET
        # create an empty form
        shipment_form = CreateShipmentForm()

    # if the request was POST, the form contains errors, if request was GET, it's an empty form
    return render(request, 'path/to/template.html', {
        'form': shipment_form
    })
最后,在您的模板中,您可以像往常一样显示表单。如果您不知道或需要表单的某些其他功能,请继续使用易碎的表单。

如果要向用户显示错误,请选中模板中的if shipment_form.errors并将其显示在模板中。

相关文章