Thursday, July 15, 2010

Sorting by more than one field in the admin interface

Someone posted to django-users asking how to order models on multiple fields, in the admin interface. While it isn't supported, it's not very hard to hack ordering into a ModelAdmin, if I take the shortcut of keeping the user interface the way it is. Fortunately, there is precedent for this: when you sort by Artists in iTunes, it sorts by Artists, then Albums, then Tracks, independent of the visual ordering of the columns in iTunes.

I wrote my example code using data from my song collection in iTunes, taking a naive approach, for practice. I selected all of the classical music in my iTunes collection, and copied and pasted it into vim, and saved it as a text file. I then wrote a method for deserializing, and a management command for reading in the file and importing the data. The importing was pretty easy, since when I pasted the songs they were separated by tabs. I unit-tested this process. Once I imported the data, I created a fixture using the dumpdata management command.

When I wrote the before admin interface, I saw that when I sorted by Artists, it already had the Albums and Tracks in order. To fix this, I went back and made my import script shuffle the rows, and dumped my fixtures again. Once I did this, I had the following admin code and the following result when sorting by Artists:

Before I figured out how to make it work, I defined a property on SongAdmin that specified how I would like to sort when sorting by the Artist and Album fields. I made it a hash with the field to sort by as the key, and a list of fields to order by as the value.

The first attempt I made was to override the queryset() method on the SongAdmin. This didn't work, because column sort gets applied after the ModelAdmin's queryset() method is called. A little searching in the django source code revealed that the column sort happens in the ChangeList view. I subclassed the ChangeList view, and made the ModelAdmin use the subclass by overriding the get_changelist() method:

Fortunately, the ChangeList had everything I needed to apply the property on SongAdmin. With this in place, the sorting worked as expected:

I posted the full example to GitHub. There are a couple of branches to aid in seeing the difference, without_special_ordering and with_special_ordering. To try it out, make sure django and django_extensions are installed, and check out the without_special_ordering branch and run python syncdb and python loaddata classical. Then run python runserver, log into the admin site, go to the Song list, and sort by Artist. The tracks should be mixed up. Then, in another terminal, check out the with_special_ordering branch and refresh the page. The tracks should be sorted.

I plan on using this in a project I'm working on to enable sorting by state and city.

1 comment:

  1. Please have a look at the comments on