Django admin: come ordinare secondo uno dei campi list_display personalizzati che non ha campo di 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() 

Come posso ordinare i clienti, a seconda del number_of_orders che hanno?

admin_order_field proprietà admin_order_field non può essere utilizzata qui, in quanto richiede un campo di database su cui ordinare. È ansible, come Django si basa sul DB sottostante per eseguire l’ordinamento? Creare un campo aggregato per contenere il numero di ordini sembra un overkill qui.

La cosa divertente: se cambi l’url a mano nel browser per ordinare su questa colonna – funziona come previsto!

Ho adorato la soluzione di Greg a questo problema, ma vorrei sottolineare che puoi fare la stessa cosa direttamente nell’amministratore:

 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' 

In questo modo si annota solo all'interno dell'interfaccia di amministrazione. Non con ogni query che fai.

Non l’ho ancora testato (sarei interessato a sapere se funziona), ma come definire un gestore personalizzato per il Customer che include il numero di ordini aggregati e quindi impostare admin_order_field su tale aggregato, vale a dire

 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: Ho appena testato questa idea e funziona perfettamente – non è richiesta alcuna sottoclass di admin di django!

L’unico modo che riesco a pensare è denormalizzare il campo. Cioè: crea un campo reale che viene aggiornato per rimanere sincronizzato con i campi da cui è derivato. Solitamente lo faccio sovrascrivendo save su eith il modello con i campi denormalizzati o il modello da cui 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[...]