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.
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:
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, calledformfield_callback
. This should be a function which takes a Django modelField
instance as an argument, and returns a formField
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 namedformfield
that you can call to return its default form field.So, something like:
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.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:
That worked for me on
python3
anddjango 1.11
Some might frown at this but to replace the date picker with your custom widget I would crate a monkeypatch app for your project and patch Django itself at runtime. Benefit of this is any 3rd-party apps will be effected as well and so present a uniform interface to the end user without having to modify the third party code:
The above is inspired from my html5monkeypatch I use as part of my projects, take a look at patch_widgets.py and patch_fields.py.
I use JQuery. You only have to look for the 'id' of the fields you want to associate with the date picker and bind them with JQuery and the right display format:
models.py
at the top of the page you render with your view:
You do want to define a custom widget, and use the widget's inner Media class to define the JS (and CSS?) files that have to be included in the page for the widget to work. If you do this right, you can make your widget completely self-contained and reusable. See django-markitup for one example of doing this (it has a reusable widget for the MarkItUp! universal markup editor).
Then use formfield_callback (see James Bennett's answer) to easily apply that widget to all DateField's in a form.