# 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 trier les clients en fonction de number_of_orders
Qu'ils ont?
La propriété admin_order_field
Ne peut pas être utilisée ici, car elle nécessite un champ de base de données pour effectuer le tri. Est-ce possible du tout, car Django s'appuie sur la base de données sous-jacente pour effectuer le tri? La création d'un champ agrégé pour contenir le nombre de commandes semble être une surpuissance ici.
Le plus amusant: si vous modifiez l'URL à la main dans le navigateur pour trier sur cette colonne - cela fonctionne comme prévu!
J'ai adoré la solution de Greg à ce problème, mais je voudrais 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 à l'intérieur de 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 Customer
qui inclut le nombre de commandes agrégées, puis de définir admin_order_field
à cet agrégat, c'est-à-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: Je viens de tester cette idée et cela fonctionne parfaitement - pas de sous-classement admin Django requis!
La seule façon dont je peux penser est de dénormaliser le champ. C'est-à-dire - créer un vrai champ qui est mis à jour pour rester synchronisé avec les champs dont il est dérivé. Je le fais généralement en remplaçant l'enregistrement sur le modèle par les champs dénormalisés ou le modèle dont il dérive:
# 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[...]