How to configure a date and datetime picker in Django using a global widget template override
Tags: django, python
Published: 2022-02-14 05:06:00

So after using Django on-and-off for close to 15 years, I recently learnt of a cool new (to me at least) trick. You can override the widget templates used to generate the field components for the generation of forms. These can be found here https://github.com/django/django/tree/main/django/forms/templates/django/forms/widgets

The biggest benefit to this, is not having to write custom forms, or widget over-rides in all of your Forms and ModelForms. Once you have changed the default widget template, all of your DateTimeFields will automatically use those settings.

Less code, less duplication.

## Enabling the template engine

Update `settings.py`

```python
INSTALLED_APPS = [
    'blog.apps.BlogConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.forms', # <- add this app to installed apps
]

# Validate APP_DIRS and DIRS are configured
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / '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',
            ],
        },
    },
]

# Add this line to change the form renderer to TemplateSetting
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
```


## Create the override templates

From your project template directory, I'm using `./templates/` create the following directories

    $ mkdir -p django/forms/widgets

so you should now have a set of directories that look like this.

```
├── templates
│  └── django
│     └── forms
│        └── widgets
```

To override the Django widgets we need to select the widget we wish to override, in this case we will be using `DateTimeField`.

Look at the original source file for [DateTime.html](https://github.com/django/django/blob/stable/4.0.x/django/forms/templates/django/forms/widgets/datetime.html) from the Django Repo.

`datetime.html`
```html
{% include "django/forms/widgets/input.html" %}
```

We can see that `datetime.html` inherits from the default `input.html`.

`input.html`
```html
<input 
  type="{{ widget.type }}"
  name="{{ widget.name }}"
  {% if widget.value != None %}
    value="{{ widget.value|stringformat:'s' }}"
  {% endif %}
  {% include "django/forms/widgets/attrs.html" %}
>
```

We will use this as our basis for our new `datetime.html`.


create a file `./django/forms/widgets/datetime.html` and add the following content.

> Note that have changed the `type="{{ widget.type }}"` to `type="datetime-local"` as defined here https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local .

```html
<input 
  type="datetime-local"
  name="{{ widget.name }}"
  {% if widget.value != None %}
    value="{{ widget.value|stringformat:'s' }}"
  {% endif %}
  {% include "django/forms/widgets/attrs.html" %}
>
```

Now simply `manage.py runserver` and you should see your browser now shows a date and time picker.

If you wish to configure a date picker, create a `date.html` file and set the type to `type="date"`.
To configure a time picker, create a `time.html` file and set the type to `type="time"`.

Congrats!