First, after some searching online, it seemed several people have experienced similar issues. I happened to run across this answer on stackoverflow for customizing widgets for all forms.
Here's my final code.
def make_custom_datefield(f):
formfield = f.formfield()
if isinstance(f, models.DateField):
formfield.widget.format = '%m/%d/%Y'
formfield.widget.attrs.update({'class':'datePicker', 'readonly':'true'})
return formfield
class ProjectForm(ModelForm):
formfield_callback = make_custom_datefield
class Meta:
model = Project
First, on ProjectForm I specify a custom callback function for handling form fields. This is actually really nice as I can apply this code to any form I want instead of marking individual fields for a form or applying the entire fix to every single model.
In make_custom_datefield I do a quick check to see if it's a model DateField instance. If it is, I do some modifying. First, I change the format of the widget so incoming data from the model match jQuery's format. It might be possible to modify jQuery to match Django, but whatever. Then I add on two custom attributes to the widget. Both of these directly map to html attributes of the input tag.
<input class="datePicker" readonly="true" type="text" id="id_dateDue" />
The datePicker class is important so I can mark this input as a jQuery calendar in the browser. I also mark it as readonly so users can't modify the date with bad formats. Marking the input as read-only is a double-edged sword as it also prevents users from quickly entering dates months or years away.
Once that is working, I just need to add some code to my page that will mark all datePicker instances in javascript as being datepicker widgets.
// on page load
$(function() {
$( ".datePicker" ).datepicker();
});
And that's it! I can now make jQuery UI datepickers the default widget on any django form I choose.