【问题标题】:Bootstrap Modal Forms Posting Twice DjangoBootstrap 模态表单发布两次 Django
【发布时间】:2020-02-17 14:02:21
【问题描述】:

我正在按照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_shipment.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);
                }
            }
        });
    };

【问题讨论】:

标签: html django bootstrap-modal


【解决方案1】:

看了source code的包,相信后端收到两个请求是正常的。

你的代码

$(document).ready(function() {

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

});

触发函数modalForm,它接受您的选择(formURL)并将函数newForm分配给点击事件。

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

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

注意最后一个参数submitForm指向下面的函数

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

最后在isFormValid函数中,你在表单中输入的所有数据都会被投递到你在action属性中定义的url中进行验证,如果没有错误,表单会被提交到完全相同的网址。

如果你深入研究这个包中的 python 代码,事情就会变得有趣。 BSModalForm 基于 mixins.pyhere 中的两个类,它表示当请求由 ajax 以外的任何东西发出时,保存使用表单数据创建的实例,否则(如果通过 ajax 调用请求)不要保存实例并返回它。这就是为什么它首先验证表单但永远不应该保存它(记住第一次调用确实是通过使用 jQuery 中的 ajax 调用发起的)。

您提到表单被保存了两次 - 尝试在保存函数的开头添加一行

print(request.is_ajax())

然后检查输出。可能是呼叫未能作为 AJAX 呼叫发送。 (如果是这种情况,请更新您的 jquery 版本或使用其他东西来进行调用,例如 axios)

如果您不喜欢事情的发生方式,几个选项(根据 MIT 许可打包):

  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 并将其显示在模板中。

【讨论】:

  • 嗨@Foocli 谢谢你的详细解释!我认为,根据您详细介绍的内容,我想选择选项 3 - 但我很好奇您所说的“直接使用 Bootstrap”是什么意思。你的意思是把 JS 完全排除在外吗?
  • 对不起,如果不清楚,我的意思是不要使用 django-bootstrap-modal-forms,只需将 bootstrap 导入您的基本模板(您可能已经完成),然后将模态写入你的模板。您应该能够从 BS 文档中复制粘贴大部分代码,但是如果验证失败,您将需要实现一个小的 js 函数来显示错误,并且由于您使用的是清晰的表单,您可能还可以找到一些此类功能的现有代码
  • 如果你真的不想写任何JS代码,也可以考虑换个地方显示错误。例如,您真的必须在模态框内显示错误吗?可以是祝酒词吗?或者它可以是页面顶部的警告
    提醒用户再试一次?请注意,这些变通办法也可能导致用户输入丢失(因此如果出现任何错误,用户可能不得不重新填写表单)
  • 嗨@Foocli 我很抱歉这可能听起来很愚蠢,但我认为我必须在 Django 中使用该包作为模态。如何将 Django 模型表单附加到模态?我可以在我的模板中包含模态的 html,但如果我想做 form.UltimateConsignee,我需要对表单进行一些参考
  • @GXM100 你能给我一个你的用例的例子吗?无需将表单附加到模态。验证,字段映射在 django 表单中是标准的。
【解决方案2】:

我有类似的问题。我的解决方案是:

class AppCreateView(BSModalCreateView):
    template_name = 'apps/app_create.html'
    form_class = UserAppForm
    success_message = 'Success: App was created.'
    success_url = reverse_lazy('dashboard')

    def form_valid(self, form):
        if not self.request.is_ajax():
            app = form.save(commit=False)
            profile = Profile.objects.get(user=self.request.user)
            app.profile = profile
            app.save()
        return HttpResponseRedirect(self.success_url)

【讨论】:

    猜你喜欢
    相关资源
    最近更新 更多
    热门标签