- type:
- text //
- 2010.Jan.1
- 10.45pm
- // tagged:
- django
- python
- django-admin
- etcetera
Django and the admin date/time picker
Author’s note: I’m using the Django trunk with all this, but the following directions at least work with the 1.1 release.
The problem
So here’s my dilemma. In developing a few various forms for Etcetera (namely a manual equipment checkout form) I’ve run into what may be the hairiest thing yet for end-users of my system: Django’s forms.DateTimeField. It’s great for validating form data to make sure it’s in a compatible YYYY-MM-DD HH:MM format to store in a date field in the database. Unfortunately for those using the system, this restrains them to a very strict format to follow, and perhaps a difficult one to remember (we do have one user who’s in ROTC and is quite comfortable with 24-hour time). So why should we be stuck with a single field (or even a split field, for that matter) where a user is confined to typing in this date/time by hand? I wanted to utilize the Django admin site’s date picker widget.
And you might ask this: why not use jQuery UI’s slick calendar? Or some other DHTML/JS-blended über-calendar to add a date picker? Simple: reduce dependencies. I don’t want to have to rely on installing any more frameworks or hacks, no matter how simple they are to implement. I want this to be as self-contained within the Django framework as possible.
The hint
Enter django.contrib.admin.widgets.AdminSplitDateTime.
It’s simple, clean, effective, easy-to-use. So I can just import the widgets package, specify widgets.AdminSplitDateTime as the my forms.DateTimeForm’s widget, and roll, right? It doesn’t seem to work like that. The widget depends on some extra JavaScript files (and CSS styles) in your django/contrib/admin/media directory, as well as the admin’s i18n script. There’s a couple of additional lines of code you’ll have to add that won’t be apparent at first sight.
The solution
I found this in a handy tip in a Google Groups discussion that implements it just the way it’s supposed to work. I’ll reiterate it here. Basically you set your form field’s widget to widgets.AdminSplitDateTime in your form class, and link in the JavaScript files from the admin’s media site into your Django templates. You’ll also have to set up a URL reference in your URLconf to the Django admin’s i18n script. Step by step, here it goes!
Step 1: Setting up your form
I’m assuming you already have your forms.py file in your app right now. For my code, I’ve got a project called etcetera and an app called checkout. To show you where it all starts, I’ve got an abbreviated version of my model, which is under checkout/models.py.
I’ve got my form, CheckoutModelForm, which uses forms.ModelForm, and overrides the two date fields. Notice how I’ve imported django.contrib.admin.widgets as adminwidgets, and that I’ve specified custom widgets for my two DateTimeFields.
On a related note, I developed the checkout app its own git branch. This gets weird switching to work on it from the master: running git checkout checkout sometimes throws me for a loop!
Step 2: A minor URLconf change (and a settings check)
Before we do anything to our templates, we’re going to add a minor change to our root URLconf. As my project is etcetera, the file I’m talking about is etcetera.urls. It’s possible you already have the admin site installed. in that case, you’ll only need to add the line for admin docs, and the line for admin i18n.
We’re also going to check your project’s settings.py file. Make sure you have your admin’s media directory set (the setting is ADMIN_MEDIA_PREFIX). Mine’s the default, /admin/. So if you don’t have it, add the following quick line to your settings.py file.
ADMIN_MEDIA_PREFIX = '/media/'
Now we’re ready to modify our templates to accept the changes brought about by this new form field.
Step 3: Tweaking the templates
You’ll probably want to take a look at my base template, which has a few blocks specified where I’m going to inject code with my specific template (the one that’ll extend this one). My base template is called root.html. For the sake of brevity, I’ve removed all the stuff that’s not really applicable to the task at hand (header/footers, a few additional stylesheets and scripts, blocks, includes, etc).
Here’s the template that my edit view renders with CheckoutModelForm. It’s simply called edit.html, and I’ve removed some stuff here for the sake of brevity as well.
You’ll notice some extra scripts specified here. Those’ll end up between the
tags of my final HTML. That{{ form.media }} bit will also add any media needed by the form.
While it’s not exactly part of my templates — it’s definitely used by them — I should go ahead and talk some of the CSS used by this date/time picker. I’ve taken some of this code right from the admin media’s widgets.css file and tweaked it. It resides at the bottom of my screen.css file. Note that while all the admin media is accessible from /media/, all my personal media for the site is from /_media/. Hope that’s not too confusing. There’s a whole section in the widgets.css file that deal with the calendar and time picker, and it’s denoted by comments so you won’t miss it. It’s pretty much the classes .calendar, .clockbox, and a couple of others. As always, with CSS, you should modify it to your liking and to get it to jive with the rest of your content. Here’s my finished product.
Sound off in the comments if you have any questions. I hope I’ve covered everything!

