One of those problems routinely experienced by Django developers is "The Admin page of ten thousand users." There are plenty of objects in our model that are owned by a specific user, and when we want to create or edit such a list, we're frequently confronted with an unsorted, difficult-to-search
As is often the case with Django, we can work backwards from a generalized display to a more specific display. Here's the source code I'm going to explain:
from django import forms
from django.contrib import admin
from django.contrib.admin import widgets
from models import *
class UserModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s, %s" % (obj.last_name, obj.first_name)
class SubscriptionAdminForm(forms.ModelForm):
user = UserModelChoiceField(
queryset = User.objects.order_by('last_name', 'first_name'))
book = forms.ModelChoiceField(
queryset = Book.objects.order_by('title'))
purchase_date = forms.SplitDateTimeField(
widget = widgets.AdminSplitDateTime)
class Meta:
model = Subscription
class SubscriptionAdmin(admin.ModelAdmin):
search_fields = ['user__last_name', 'user__first_name', 'book__title']
form = SubscriptionAdminForm
admin.site.register(Subscription, SubscriptionAdmin)
Starting at the bottom, I register the Subscription object with a ModelAdmin to the admin system. I override the standard ModelAdmin with two options. The first specifies the order in which objects on the Subscription list page will be searched when the user searches for anything. This helps find a specific subscription. Once you've found the subscription you want to manage, you go to the Subscription detail page.
It's rare (but possible) that the administrator wants to change which user owns a specific subscription, but it's more common that an administrator may want to comp a subscription to a user. First you have to find that user. To do that, you can let the Admin create its own generic form, but that form will be populated with users ordered by the default order specified in Auth (and there is no default order, so the order is deterministic, but useless to you and me).
Instead, I create a SubscriptionAdminForm, and tell the SubscriptionAdmin to use it instead. In that form, I first specify that this form is a ModelForm for model Subscription. "book" is made into a ModelChoiceField, but I manually specify an order for the queryset. "purchase_date" uses the SplitDateTimeField, but I use the Django admin's AdminSplitDateTime widget, to maintain visual consistency with the rest of the admin. For the User, I could start with a standard ModelChoiceField, but that would make the labels come out "first_name last_name," which is not search-ready via standard key searches. You're likely to have a lot of "Roberts" scattered throughout the system, but only a few "Smiths," and it would be nice if they were all near each other.
So I override the ModelChoiceField and change the label_from_instance() method to return something other that the standard representation; in this case, I want the format "last name, first name."
And there you go: Books ordered by title, users ordered by Last Name, First Name (and displayed that way), and the whole thing displayed in a Django Admin friendly way.
Little bits of pieces of this stuff can be found scattered throughout the Django Book, the Django docs, and the source code, but this blog entry provides a concise summary of the basic features you might want in a customized Admin form. Consider this as a template for improving the experience for the poor schlubs who have to use your Admin page every day.