Rendering a Bootstrap Datepicker inside a modal using the bootstrap-datepicker-plus package for Django can sometimes cause some issues based on the order your page loads your scripts. Using a context processor that gives every template access to the form’s media should solve the issue.
I am using Django 3.1 and using Javascript and Ajax to load and submit the form. Here is a quick tutorial:
1. Install Django Bootstrap Datepicker using pip install django-bootstrap-datepicker-plus and follow the package installation instructions
2. Create your form and load the DatePicker widget. In this example I am using a generic Django Form. If you’re using a Model Form, follow the instructions from the package installation link above
from django import forms
from bootstrap_datepicker_plus import DatePickerInput
EVENT_TYPE_CHOICES = [
("Wedding", "Wedding"),
("Bar/Bat Mitzvah", "Bar/Bat Mitzvah"),
("Corporate", "Corporate"),
("Private Party", "Private Party"),
]
class RequestQuoteForm(forms.Form):
name = name = forms.CharField(max_length=100)
email = forms.EmailField()
phone = forms.CharField(max_length=15)
event_date = forms.DateField(widget=DatePickerInput(format="%m/%d/%Y"), label="Event Date")
event_type = forms.ChoiceField(choices=EVENT_TYPE_CHOICES, label="Event Type")
3. Create a Context Processor
Inside your app directory create a new file called context_processors.py
from .forms import RequestQuoteForm
def quote_form_processor(request):
quote_form = RequestQuoteForm()
return {'quote_form': quote_form}
4. In your settings.py add the context processor to your TEMPLATES
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [str(BASE_DIR.joinpath("templates"))],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"pages.context_processors.quote_form_processor",
],
},
},
]
5. Load the media in your base.html template. NOTE: Make sure the form name matches the name you set in your context processor.
{{quote_form.media}}
6. Write your function based view
from django.http import JsonResponse
from django.template.loader import render_to_string
def request_quote(request):
data = dict()
if request.method == "POST":
form = RequestQuoteForm(request.POST or None)
if form.is_valid():
data["html_success_message"] = render_to_string(
"pages/includes/partial_quote_submit_success.html", request=request,
)
data["form_is_valid"] = True
else:
data["form_is_valid"] = False
else:
quote_form = RequestQuoteForm()
data["html_form"] = render_to_string(
"pages/includes/partial_quote_form.html",
{"quote_form": quote_form},
request=request,
)
return JsonResponse(data)
7. In your static folder create a file named custom.js and load file in the footer of your base.html template
$(function () {
/* Functions */
var loadForm = function () {
var btn = $(this);
$.ajax({
url: btn.attr("data-url"),
type: 'get',
dataType: 'json',
beforeSend: function () {
$("#modal-base .modal-content").html("");
$("#modal-base").modal("show");
},
success:function (data) {
$("#modal-base .modal-content").html(data.html_form);
}
});
};
var saveForm = function () {
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
type: form.attr("method"),
dataType: 'json',
success: function (data) {
if (data.form_is_valid) {
$("#modal-base .modal-content").html(data.html_success_message);
}
else {
$("#modal-base .modal-content").html(data.html_form);
}
}
});
returnfalse;
};
/* Binding */
// Request Quote
$(".js-quote-request").on("click", loadForm);
$("#modal-base").on("submit", ".js-quote-request-form", saveForm);
});
8. Create your form template and load the form media again
{{quote_form.media}}
<form method="POST" action="{% url 'request_quote' %}" class="js-quote-request-form" id="quoteForm">
{% csrf_token %}
<div class="modal-header">
<h4 class="modal-title">Request a Quote</h4>
</div>
<div class="modal-body">
{% bootstrap_form_errors quote_form %}
{% bootstrap_form quote_form %}
</div>
<div class="modal-footer">
<button type="button"class="btn btn-default" data-dismiss="modal">Close</button>
{% bootstrap_button "Schedule" button_type="submit" button_class="btn-primary"%}
</div>
</form>
9. Create your success template
<div class="modal-header">
<h4 class="modal-title">Quote Request Sent</h4>
</div>
<div class="modal-body">
Thank you for submitting your form! We will be in touch soon!
</div>
<div class="modal-footer">
<button type="button"class="btn btn-default"data-dismiss="modal">Close</button>
</div>
10. Add the modal to your base.html template
<div class="modal fade" id="modal-base">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
</div>
</div>
</div>
11. Render your modal from any template using a button
<button data-url="{% url 'request_quote' %}" class="btn btn-lg btn-primary js-quote-request">Request a Quote</button>