Administrateur Django: comment sortinger par l’un des champs list_display personnalisés sans champ de firebase 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() 

Comment puis-je sortinger les clients en fonction du nombre de commandes qu’ils ont?

admin_order_field propriété admin_order_field ne peut pas être utilisée ici, car elle nécessite un champ de firebase database pour sortinger. Est-ce possible, car Django s’appuie sur la firebase database sous-jacente pour effectuer le sorting? Créer un champ d’agrégat pour contenir le nombre d’ordres semble être exagéré ici.

La chose amusante: si vous changez l’url à la main dans le navigateur pour sortinger cette colonne, cela fonctionne comme prévu!

J’ai adoré la solution de Greg à ce problème, mais j’aimerais souligner que vous pouvez faire la même chose directement dans l’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' 

De cette façon, vous annotez uniquement dans l'interface d'administration. Pas avec chaque requête que vous faites.

Je n’ai pas testé cela (je serais intéressé de savoir si cela fonctionne) mais qu’en est-il de la définition d’un gestionnaire personnalisé pour le Customer qui inclut le nombre de commandes agrégées, puis en définissant admin_order_field sur cet agrégat, c.-à-d.

 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: Je viens de tester cette idée et elle fonctionne parfaitement – aucun sous-classement administrateur de django n’est requirejs!

La seule façon de penser est de dénormaliser le champ. C’est-à-dire créer un champ réel qui est mis à jour pour restr en phase avec les champs dont il est dérivé. Je le fais habituellement en remplaçant le modèle par les champs dénormalisés ou le modèle dérivé de:

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