Solution 1 :

Update to this post: It seems like that the package was updated and the original issue was solved, thus links in this answer won’t point you to the correct codes/comments anymore. If you are still experiencing the same problem, try upgrading the package first.

After reading the source code for the package, I believe that receiving two requests on the backend is normal.

Your code

$(document).ready(function() {

        formURL: "{% url 'CreateShipmentView' %}"


triggers the function modalForm where it takes your options (formURL) and assign the function newForm to a click event.

Then in the newForm function it calls the function addListeners to bind a click event to the submit button in the modal, and the event is called as following:

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

Note that the last parameter submitForm points to the following function

var submitForm = function(modalForm) {

Finally in the function isFormValid, all the data you entered in the form will be posted to the url you defined in the action attribute for validation, and if there is no error, the form will be submitted to the very same url.

If you dig into the python code in this package, things become interesting. The BSModalForm is based on two classes in and in here it says that when the request was made by anything other than ajax, save the instance created using the form data, or otherwise (if requested by ajax call) don’t save the instance and return it. That’s why it validated the form at first but should never save it (remember the first call was indeed originated by using the ajax call in jQuery).

You mentioned that the form was being saved twice – try to add a line at the beginning of the save function as


and then check the output. it could be that the call failed to be sent as an AJAX call. (if that’s the case, update your jquery version or use something else to make the call like axios)

If you don’t like how things are happening, few options (package under MIT license):

  1. Change the save function to validate the instance then save it as a normal django save function, but that involves changing some of the JS code.

  2. Make an API endpoint to receive the data and communicate using json instead of returning html code every time (I guess this is also the reason why the author wrote the JS in the current way because you then face the rendering issue). Since currently you don’t need to perform any other action after submitting the form, it doesn’t make sense to return the instance anymore. (no need for DRF as there is a JsonResponse class built-in in django and if you only need this one endpoint)

  3. Use BootStrap directly since the story here is rather simple: a modal on the page, a button to trigger the modal, a form in the modal and you can submit it. You might need to write some of your own JS for error displaying but it should still be easier than changing the existing package.

An example

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']

view for rendering the form (GET) and receiving form submissions (POST)

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 =
            # 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
        # 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

Finally in your template, you can just display the form as you would normally. If you don’t know or need some other features for your form, keep using crispy forms.

check if shipment_form.errors in your template and display it in your template if you want to show errors to the users.

Solution 2 :

I had similar problem. My solution was:

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 =
            profile = Profile.objects.get(user=self.request.user)
            app.profile = profile
        return HttpResponseRedirect(self.success_url)

Problem :

I am following the instructions from django-bootstrap-modal-forms and what I am finding is that my form is posting or submitting twice when I submit the form. First, the object was simply being created twice, I could see that from the admin. Now it seems the form is creating the object, but also entering its validation state, which I obviously don’t want if the form was successful, which it is.

Has anyone experienced this? I’ve done nothing more than what was outlined in the docs that I linked to and I cannot identify any reason why this should be submitting twice.

Here is my code:


<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">


$(document).ready(function() {

        formURL: "{% url 'CreateShipmentView' %}"


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 (template for modal form)

{% 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>

  <div class="modal-body">

  <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>


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

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

Event Listeners on Submit Button

 // 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) {
            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
                    $(modalForm).attr("action", formURL);
                    // Reinstantiate click listener on submitBtn
                    ajaxSubmit(modalID, modalContent, modalForm, formURL, errorClass, submitBtn);
                } else {
                    // Form is valid, submit it


Comment posted by Foocli

Check the source code after rendering the page to see if you have two elements being rendered with class “create-shipment”, and what if you change tag to