2010-01-30 10 views
95
# 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 avrei potuto ordinare I clienti, a seconda number_of_orders hanno?Django admin: come ordinare da uno dei campi list_display personalizzati che non ha alcun campo del database

admin_order_field La proprietà non può essere utilizzata qui poiché richiede un campo di database su cui ordinare. È possibile, 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 si modifica l'url a mano nel browser per ordinare su questa colonna, funziona come previsto!

+0

"La cosa divertente: se si modifica l'url a mano nel browser per ordinare su questa colonna, funziona come previsto!" Intendi:/admin/myapp/cliente /? Ot = asc & o = 2 Sei sicuro? –

+0

sì, sia asc che dsc. Forse funziona solo con decimali. –

+0

Non penso che funzionerebbe con più pagine. –

risposta

120

Ho amato la soluzione di Greg a questo problema, ma vorrei sottolineare che si può fare la stessa cosa direttamente in 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' 

questo modo di annotare solo all'interno l'interfaccia di amministrazione. Non con ogni query che fai.

+2

Sì, questo è un modo molto migliore. :) – Greg

+2

C'è una [modifica suggerita] (http://stackoverflow.com/suggested-edits/240451) su questa risposta. Ho votato per respingerlo perché ha rimosso troppo testo. Non conosco Django, non ho idea se il codice proposto sia degno di nota. – Gilles

+6

I campi personalizzati list_display accettano 2 argomenti: 'self' e' obj'. 'number_of_orders' dovrebbe essere' def number_of_orders (self, obj): ' – Eric

0

L'unico modo che posso pensare è denormalizzare il campo. Cioè: crea un campo reale che viene aggiornato per rimanere sincronizzato con i campi da cui è derivato. Io di solito faccio sovrascrivendo Salva sulla eith il modello con i campi denormalizzati o il modello che deriva da:

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

Questo dovrebbe funzionare, ma non può essere contrassegnato come accettato a causa del campo aggiuntivo DB coinvolto. Si noti inoltre mancante .count() alla fine della riga di query-set. –

+0

corretto il conteggio(). L'unica altra soluzione (a parte la sottoclasse di grossi pezzi di contrib.admin) sarebbe un hack Jquery/Ajaxy. –

41

non ho ancora testato questo fuori (sarei interessato a sapere se funziona), ma ciò che sulla definizione di un responsabile personalizzato per Customer che comprende il numero di ordini aggregati, e quindi impostando admin_order_field a tale aggregato, cioè

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 - senza sottoclassi django amministratore richiesto !

+0

basta leggere la documentazione :) – Vladimir

Problemi correlati