How do you change the default widget for all Django date fields in a ModelForm?
Given a set of typical models:
# Application A
from django.db import models
class TypicalModelA(models.Model):
the_date = models.DateField()
# Application B
from django.db import models
class TypicalModelB(models.Model):
another_date = models.DateField()
...
How might one change the default widget for all DateFields to a custom MyDateWidget?
I'm asking because I want my application to have a jQueryUI datepicker for inputting dates.
I've considered a custom field that extends django.db.models.DateField with my custom widget. Is this the best way to implement this sort of across-the-board change? Such a change will require specifically importing a special MyDateField into every model, which is labour intensive, prone to developer error (i.e. a few models.DateField's will get through), and in my mind seems like unnecessary duplication of effort. On the other hand, I don't like modifying what could be considered the canonical version of models.DateField.
Thoughts and input is appreciated.
Solution 1:
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...
Solution 2:
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