Date Field With Calendar Widget in Django Forms

/images/date-field-with-calendar-widget-in-django-forms/date-field-with-calendar-widget-in-django-forms.gif

All modern web browsers are able to display a calendar widget in the date fields of a form. However, in order to take advantage of this feature within a Django project, there are some caveats to be taken into consideration, especially to avoid errors in form validation. Here's how to do it.

First, we need a Django form with a date field (a forms.DateField instance). For example:

# Usually in forms.py
from django import forms
class MyForm(forms.Form):
    name = forms.CharField(
        label="Name",
        required=True
    )
    date_of_birth = forms.DateField(
        label="Date of Birth",
        required=True,
        widget=forms.DateInput(format="%Y-%m-%d", attrs={"type": "date"}),
        input_formats=["%Y-%m-%d"]
    )

The former code defines a form that asks the user for their name and date of birth. The field we are interested in is date_of_birth. Line number 13 tells Django that it should generate the HTML tag with the type="date" attribute, ensuring the browser renders a date input for which a calendar widget will be displayed after clicking on it. In fact, this is the HTML code generated by Django for the date_of_birth field:

<input type="date" name="date_of_birth" required="" id="id_date_of_birth" wfd-id="id2">

Second, it is a must that the date format configured in the Django form (lines 13 and 14) matches the format defined by the HTML standard, namely yyyy-mm-dd, which according to Python's format codes is expressed as %Y-%m-%d. This allows Django to correctly validate the submitted date and convert it to an instance of the datetime.date standard class.

Once the form is defined, thirdly we must create a view to display and validate it, and to make something useful out of the submitted data. Something like this:

import datetime
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from .forms import MyForm
def form_view(request: HttpRequest) -> HttpResponse:
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            # Receive the submitted date as an instance of `datetime.date`.
            date: datetime.date = form.cleaned_data["date_of_birth"]
            # Do something with that value, such as storing it in a database
            # or displaying it to the user.
            date_output = date.strftime("%d %b %Y")
            return HttpResponse(f"The submitted date is: {date_output}")
    else:
        form = MyForm()
    ctx = {"form": form}
    return render(request, "myapp/form.html", ctx)

The key thing here is that Django returns the date submitted by the user as a standard datetime.datetime object, so we can leverage the multiple operations provided by the datetime module.

For sake of completeness, don't forget the template in which the form is displayed. In the example view, we use a file called form.html inside the templates/myapp directory (following the project structure outlined in Creating and Setting Up a Django Project):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Django Form With Date Field and Calendar Widget</title>
</head>
<body>
    <h3>Django Form With Date Field and Calendar Widget</h3>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Submit">
    </form>
</body>
</html>

And the URL settings in urls.py:

from django.urls import path
from . import views
urlpatterns = [
    path("form", views.form_view, name="form_view")
]