Django admin: how to sort by one of the custom list_display fields that has no database field Django admin: how to sort by one of the custom list_display fields that has no database field django django

Django admin: how to sort by one of the custom list_display fields that has no database field


I loved Greg's solution to this problem, but I'd like to point that you can do the same thing directly in the admin:

from django.db import modelsclass CustomerAdmin(admin.ModelAdmin):    list_display = ('number_of_orders',)    def get_queryset(self, request):    # def queryset(self, request): # For Django <1.6        qs = super(CustomerAdmin, self).get_queryset(request)        # qs = super(CustomerAdmin, self).queryset(request) # For Django <1.6        qs = qs.annotate(models.Count('order'))        return qs    def number_of_orders(self, obj):        return obj.order__count    number_of_orders.admin_order_field = 'order__count'

This way you only annotate inside the admin interface. Not with every query that you do.


I haven't tested this out (I'd be interested to know if it works) but what about defining a custom manager for Customer which includes the number of orders aggregated, and then setting admin_order_field to that aggregate, ie

from django.db import models class CustomerManager(models.Manager):    def get_query_set(self):        return super(CustomerManager, self).get_query_set().annotate(models.Count('order'))class Customer(models.Model):    foo = models.CharField[...]    objects = CustomerManager()    def number_of_orders(self):        return u'%s' % Order.objects.filter(customer=self).count()    number_of_orders.admin_order_field = 'order__count'

EDIT: I've just tested this idea and it works perfectly - no django admin subclassing required!


The only way I can think of is to denormalize the field. That is - create a real field that get's updated to stay in sync with the fields it is derived from. I usually do this by overriding save on eith the model with the denormalized fields or the model it derives from:

# models.pyclass Order(models.Model):    bar = models.CharField[...]    customer = models.ForeignKey(Customer)    def save(self):        super(Order, self).save()        self.customer.number_of_orders = Order.objects.filter(customer=self.customer).count()        self.customer.save()class Customer(models.Model):    foo = models.CharField[...]    number_of_orders = models.IntegerField[...]