Django admin: como ordenar por um dos campos customizados list_display que não possui um campo de database

# admin.py class CustomerAdmin(admin.ModelAdmin): list_display = ('foo', 'number_of_orders') # models.py class Order(models.Model): bar = models.CharField[...] customer = models.ForeignKey(Customer) class Customer(models.Model): foo = models.CharField[...] def number_of_orders(self): return u'%s' % Order.objects.filter(customer=self).count() 

Como eu poderia classificar os clientes, dependendo do número de number_of_orders eles têm?

admin_order_field propriedade admin_order_field não pode ser usada aqui, pois requer um campo de database para classificar. É possível, como o Django depende do database subjacente para realizar a sorting? Criar um campo agregado para conter o número de pedidos parece um exagero aqui.

A coisa divertida: se você mudar o URL manualmente no navegador para classificar nesta coluna – ele funciona como esperado!

Eu amei a solução de Greg para este problema, mas gostaria de apontar que você pode fazer a mesma coisa diretamente no admin:

 from django.db import models class 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' 

Desta forma, você só faz annotations dentro da interface de administração. Não com todas as consultas que você faz.

Eu não testei isso (eu estaria interessado em saber se funciona), mas o que dizer de definir um gerenciador customizado para o Customer que inclui o número de pedidos agregados e, em seguida, configurar admin_order_field para esse agregado, ou seja,

 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: Acabei de testar essa idéia e funciona perfeitamente – não é necessário subsorting admin django!

A única maneira que posso pensar é desnormalizar o campo. Isto é – crie um campo real que seja atualizado para ficar em sincronia com os campos dos quais ele é derivado. Eu costumo fazer isso substituindo save no modelo com os campos desnormalizados ou o modelo do qual deriva:

 # models.py class 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[...]