How do you change the default widget for all Django date fields in a ModelForm?

Well, making a custom model field just to change it's default form widget is not really the obvious place to start.

You can make your own form widget and override the field in the form, specifying your own widget like in Soviut's answer.

There's also a shorter way:

class ArticleForm(ModelForm):
     pub_date = DateField(widget=MyDateWidget())

     class Meta:
         model = Article

There is an example of how to write form widgets, it's somewhere in the forms package of Django. It's a datepicker with 3 dropdowns.

What I usually do when I just want to add some JavaScript to a standard HTML input element is leave it the way it is and modify it by referencing it's id later with JavaScript. You can easily catch the naming convention for the ids of the input fields Django generates.

You can also just provide the class for the widget when you override it in the form. Then catch them all with jQuery by the class name.


You can declare an attribute on your ModelForm class, called formfield_callback. This should be a function which takes a Django model Field instance as an argument, and returns a form Field instance to represent it in the form.

Then all you have to do is look to see if the model field passed in is an instance of DateField and, if so, return your custom field/widget. If not, the model field will have a method named formfield that you can call to return its default form field.

So, something like:

def make_custom_datefield(f):
    if isinstance(f, models.DateField):
        # return form field with your custom widget here...
    else:
        return f.formfield(**kwargs)

class SomeForm(forms.ModelForm)
    formfield_callback = make_custom_datefield

    class Meta:
        # normal modelform stuff here...

This article has helped me numerous times.

The meat of it involves overriding the ModelForm's __init__ method, then calling the super class' __init__ method, then adjusting the fields individually.

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        self.fields['question'].widget = forms.Textarea()

    class Meta:
        model = Poll

This method may seem more complicated than Vasil's, but it offers the additional benefit of being able to precisely override any attribute on a field without resetting any other attributes by re-declaring it.

UPDATE: Suggested approach could be generalized to change all date fields without typing each name strictly:

from django.forms import fields as formfields
from django.contrib.admin import widgets

class PollForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(PollForm, self).__init__(*args, **kwargs)
        for field_name in self.fields:
            field = self.fields[field_name]
            if isinstance(field, formfields.DateField):
                field.widget = widgets.AdminDateWidget()

    class Meta:
        model = Poll

That worked for me on python3 and django 1.11