views:

844

answers:

3

I have a model that has a ForeignKey to the built-in user model in django.contrib.auth and I'm frustrated by the fact the select box in the admin always sorts by the user's primary key.

I'd much rather have it sort by username alphabetically, and while it's my instinct not to want to fiddle with the innards of Django, I can't seem to find a simpler way to reorder the users.

The most straightforward way I can think of would be to dip into my Django install and add

ordering = ('username',)

to the Meta class of the User model.

Is there some kind of monkeypatching that I could do or any other less invasive way to modify the ordering of the User model?

Alternatively, can anyone thing of anything that could break by making this change?

A: 

Nothing will break by making this change, but the absence of a default ordering is intentional (for performance reasons). See http://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models:

You could also use a proxy model to define a different default ordering on a model. The standard User model has no ordering defined on it (intentionally; sorting is expensive and we don't want to do it all the time when we fetch users). You might want to regularly order by the username attribute when you use the proxy. This is easy:

class OrderedUser(User):
    class Meta:
        ordering = ["username"]
        proxy = True

In order to change that behaviour in the admin, you will have to write your own authentification backend, as explained in http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/.

Arnaud
+8  A: 

There is a way using ModelAdmin objects to specify your own form. By specifying your own form, you have complete control over the form's composition and validation.

Say that the model which has an FK to User is Foo.

Your myapp/models.py might look like this:

from django.db import models
from django.contrib.auth.models import User

class Foo(models.Model):
    user = models.ForeignKey(User)
    some_val = models.IntegerField()

You would then create a myapp/admin.py file containing something like this:

from django.contrib.auth.models import User
from django import forms
from django.contrib import admin

class FooAdminForm(forms.ModelForm):
    user = forms.ModelChoiceField(queryset=User.objects.order_by('username'))

    class Meta:
        model = Foo

 class FooAdmin(admin.ModelAdmin):
     form = FooAdminForm

 admin.site.register(Foo, FooAdmin)

Once you've done this, the <select> dropdown will order the user objects according to username. No need to worry about to other fields on Foo... you only need to specify the overrides in your FooAdminForm class. Unfortunately, you'll need to provide this custom form definition for every model having an FK to User that you wish to present in the admin site.

Jarret Hardie
this worked great for me
Rasiel
A: 

Jarret's answer above should actually read:

from django.contrib.auth.models import User
from django.contrib import admin
from django import forms
from yourapp.models import Foo

class FooAdminForm(forms.ModelForm):
    class Meta:
        model = Foo

    def __init__(self, *args, **kwds):
        super(FooAdminForm, self).__init__(*args, **kwds)
        self.fields['user'].queryset = User.objects.order_by(...)

class FooAdmin(admin.ModelAdmin):
    # other stuff here
    form = FooAdminForm

admin.site.register(Foo, FooAdmin)

so the queryset gets re-evaluated each time you create the form, as opposed to once, when the module containing the form is imported.

elo80ka