How to use a DatePicker in a ModelForm in django?
Asked Answered
C

4

7

I am using django 3.0 and I am trying to display a datepicker widget in my ModelForm, but I can't figure out how (all I can get is text field). I have tried looking for some solutions, but couldn't find any. This is how my Model and my ModelForm look like:

class Membership(models.Model):
  start_date = models.DateField(default=datetime.today, null=True)
  owner = models.ForeignKey(Client, on_delete=models.CASCADE, null=True)
  type = models.ForeignKey(MembershipType, on_delete=models.CASCADE, null=True)

class MembershipForm(ModelForm):
  class Meta:
    model = Membership
    fields = ['owner', 'start_date', 'type']
    widgets = {
        'start_date': forms.DateInput
    }

And this is my html:

<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Cran answered 7/4, 2020 at 10:21 Comment(3)
This is the expected behavior: the DateInput is just a text box with an optional format: docs.djangoproject.com/en/3.0/ref/forms/widgets/#dateinputSkell
I know that, but I was wondering how can I use the DatePicker widget.Cran
You could use the admin datepicker, as described here and here.Bullshit
S
0

This is the expected behavior. A DateInput widget [Django-doc] is just a <input type="text"> element with an optional format parameter.

You can make use of a package, like for example django-bootstrap-datepicker-plus [pypi] , and then define a form with the DatePickerInput:

from bootstrap_datepicker_plus import DatePickerInput

class MembershipForm(ModelForm):
  class Meta:
    model = Membership
    fields = ['owner', 'start_date', 'type']
    widgets = {
        'start_date': DatePickerInput
    }

In the template you will need to render the media of the form and load the bootstrap css and javascript:

{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ form.media }}

<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Skell answered 7/4, 2020 at 10:36 Comment(13)
saying, invalid block {5 bootstrap_css %} and {% bootstrap_javascript jquery='full' %}Cordes
@djangodeveloper: that is because you wrote {5 instead of {%...Skell
oh, sorry, removed but still facing the issue. what I want to do is, update a form in which there are datepicker options, at the moment not getting the datepicker workedCordes
@djangodeveloper: what issue?Skell
Invalid block tag on line 3: 'bootstrap_css'Cordes
@djangodeveloper: you need to {% load bootstrap4 %} first to load the template tag...Skell
'bootstrap' is not a registered tag library. Must be one of:Cordes
when I load bootstrap_tags, it doesn't throws an error related to bootstrap is not a registered tag libraryCordes
and same goes for bootstrap4 or bootstrap5Cordes
@djangodeveloper: you need to install the package first, and add it to the INSTALLED_APPS. See the installing section of pypi.org/project/django-bootstrap-datepicker-plusSkell
installed it. now getting issue of 'bootstrap_javascript' received unexpected keyword argument 'jquery'Cordes
@djangodeveloper: what if you remove jquery='full'Skell
I had to downgrade the bootstrap version. jquery = "Full" was working with bootstrap4 only. in bootstrap5, it need to be removedCordes
B
15

Although @willem-van-onsem's answer is great, there are a few alternatives that do not require additional dependencies.

A few options, in order of increasing effort:

  1. Use a SelectDateWidget instead of the default DateInput (no JavaScript required):

    class MyForm(forms.Form):
        date = forms.DateField(widget=forms.SelectDateWidget())
    
  2. Use the browser's built-in date picker, by implementing a customized widget that uses the HTML <input type="date"> element (no JavaScript required):

    class MyDateInput(forms.widgets.DateInput):
        input_type = 'date'
    
    class MyForm(forms.Form):
        date = forms.DateField(widget=MyDateInput())
    

    or, alternatively:

    class MyForm(forms.Form):
        date = forms.DateField(widget=forms.DateInput(attrs=dict(type='date')))
    
  3. Use the date picker from django.contrib.admin, as described here in detail. In short, there are a few things you would need:

    from django.contrib.admin.widgets import AdminDateWidget
    ...
    class MyForm(forms.Form):
        date = forms.DateField(widget=AdminDateWidget())
    

    then, to make this work, add the following dependencies to your template <head>:

    <link rel="stylesheet" type="text/css" href="{% static 'admin/css/widgets.css' %}" />
    <script src="{% static 'admin/js/core.js' %}"></script>
    <script src="{% url 'admin:jsi18n' %}"></script>  {# see note below #}
    {{ form.media }}  {# this adds 'calendar.js' and 'DateTimeShortcuts.js' #}   
    

    Now there's one catch: the admin:jsi18n url only works for users with admin access, so you may need to replace this and define an alternative path in your urls.py, e.g.:

    from django.views import i18n
    ...
    urlpatterns = [
        ...,
        path('jsi18n/', i18n.JavaScriptCatalog.as_view(), name='jsi18n'),
    ]
    

Finally, here's what the widgets look like (on firefox):

date picker examples

Personally I like the second option best. It also allows us to specify initial, minimum and maximum values (in django you can do this e.g. using the attrs argument). Here's a quick snippet to show the HTML element in action:

<input type="date" value="2021-09-09" min="2021-09-09">
Bullshit answered 8/9, 2021 at 18:30 Comment(1)
N
1

Django 4.0. Leaving this here incase it helps someone else. This will set the minimum date and default value to today's date and should be used in forms.py. In my case I use crispy forms in my .html to render the field.

from datetime import date

today = date.today()
    
class DateForm(forms.ModelForm):
   target_Date = forms.DateField(widget=forms.TextInput(attrs={'min': today, 'value': today, 'type': 'date'}), required=True)

class Meta:
    model = DateForm
    fields = ['target_Date']
Nahama answered 24/5, 2022 at 16:13 Comment(0)
S
0

This is the expected behavior. A DateInput widget [Django-doc] is just a <input type="text"> element with an optional format parameter.

You can make use of a package, like for example django-bootstrap-datepicker-plus [pypi] , and then define a form with the DatePickerInput:

from bootstrap_datepicker_plus import DatePickerInput

class MembershipForm(ModelForm):
  class Meta:
    model = Membership
    fields = ['owner', 'start_date', 'type']
    widgets = {
        'start_date': DatePickerInput
    }

In the template you will need to render the media of the form and load the bootstrap css and javascript:

{% load bootstrap4 %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
{{ form.media }}

<form class="container" action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Skell answered 7/4, 2020 at 10:36 Comment(13)
saying, invalid block {5 bootstrap_css %} and {% bootstrap_javascript jquery='full' %}Cordes
@djangodeveloper: that is because you wrote {5 instead of {%...Skell
oh, sorry, removed but still facing the issue. what I want to do is, update a form in which there are datepicker options, at the moment not getting the datepicker workedCordes
@djangodeveloper: what issue?Skell
Invalid block tag on line 3: 'bootstrap_css'Cordes
@djangodeveloper: you need to {% load bootstrap4 %} first to load the template tag...Skell
'bootstrap' is not a registered tag library. Must be one of:Cordes
when I load bootstrap_tags, it doesn't throws an error related to bootstrap is not a registered tag libraryCordes
and same goes for bootstrap4 or bootstrap5Cordes
@djangodeveloper: you need to install the package first, and add it to the INSTALLED_APPS. See the installing section of pypi.org/project/django-bootstrap-datepicker-plusSkell
installed it. now getting issue of 'bootstrap_javascript' received unexpected keyword argument 'jquery'Cordes
@djangodeveloper: what if you remove jquery='full'Skell
I had to downgrade the bootstrap version. jquery = "Full" was working with bootstrap4 only. in bootstrap5, it need to be removedCordes
E
0

As other have said, this is expected since its just a special text field.

An alternative I prefer is using django-widget-tweaks as this pushes front-end customizations back to your template instead of editing forms.py on the backend. Saving/testing is also faster since the app doesn't have to reload with each save.

Install to your environment:

pip install django-widget-tweaks

Add to installed apps:

INSTALLED_APPS = [
   ...
   "widget_tweaks",
]

Add to your template:

{% extends 'app/base.html' %}
{% load widget_tweaks %}

Use render_field with input tag attributes to customize your field. Eg below using bootstrap 5. Notice how we can specify attributes such as type and class within the template tag:

<div class="col-2">
   <label for="{{ form.date.id_for_label }}" class="col-form-label">{{ form.year.label }}</label>
</div>
<div class="col-4">
   {% render_field form.year type="date" class="form-control" placeholder="mm/dd/yyyy" %}
<div>

widget tweaks date

Ecclesiolatry answered 25/3, 2022 at 21:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.