Django admin: veritabanı alanı olmayan özel list_display alanlarından birine göre sıralama nasıl yapılır


122
# 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()  

Sahip number_of_ordersolduklarına bağlı olarak Müşterileri nasıl sıralayabilirim ?

admin_order_fieldözelliği, sıralama için bir veritabanı alanı gerektirdiğinden burada kullanılamaz. Django, sıralama yapmak için temeldeki DB'ye dayandığından, bu mümkün müdür? Sipariş sayısını içerecek bir toplu alan oluşturmak burada aşırı bir şey gibi görünüyor.

İşin eğlenceli yanı, bu sütunda sıralamak için tarayıcıda URL'yi elle değiştirirseniz, beklendiği gibi çalışır!


"İşin eğlenceli yanı: bu sütunda sıralamak için tarayıcıda url'yi elle değiştirirseniz - beklendiği gibi çalışır!" Şunu demek istiyorsun: / admin / myapp / customer /? Ot = asc & o = 2 Emin misin?
Andy Baker

evet, hem asc hem de dsc. Belki sadece ondalık sayılarla çalışır.
mike_k

Birden fazla sayfayla çalışacağını sanmıyorum.
Chase Seibert

Yanıtlar:


159

Greg'in bu soruna verdiği çözümü sevdim, ancak aynı şeyi doğrudan yönetici içinde yapabileceğinizi belirtmek isterim:

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'

Bu şekilde, yalnızca yönetici arayüzünde açıklama yaparsınız. Yaptığınız her sorguda değil.


5
Evet, bu çok daha iyi bir yol. :)
Greg

2
Bir var önerdi düzenleme bu cevabına. Çok fazla metin çıkardığı için reddetmek için oy verdim. Django'yu bilmiyorum, önerilen kod değişikliğinin bahsetmeye değer olup olmadığı hakkında hiçbir fikrim yok.
Gilles 'SO- kötü olmayı bırak'

1
@Gilles Önerilen düzenleme daha basit bir number_of_orders tanımı hakkında doğrudur. Bu çalışır: def number_of_orders(self, obj): return obj.order__count
Nils

1
Bunun get_queryset()yerine olamaz queryset()mı?
Mariusz Jamro

2
get_queryset olmalıdır (self, request): ... için Django 1.6+
michael

50

Bunu test etmedim (işe yarayıp yaramadığını bilmek isterim) peki ya Customerkendisi için toplanan siparişlerin sayısını içeren özel bir yönetici tanımlayıp admin_order_fieldbu toplamı ayarlamaya ne dersiniz?

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'

DÜZENLEME: Bu fikri henüz test ettim ve mükemmel çalışıyor - django yönetici alt sınıfına gerek yok!


1
Bu, kabul edilene kıyasla daha iyi bir cevaptır. Kabul edileni uygularken karşılaştığım sorun, yönetici düzeyinde bu güncellenmiş sorgu kümesiyle birlikte bir şey aradığınızda, çok fazla zaman alır ve ayrıca bulduğu sonuçlar için yanlış sayımla gelir.
Mutant

0

Aklıma gelen tek yol, alanı normal dışı bırakmak. Yani - türetildiği alanlarla senkronize kalması için güncellenen gerçek bir alan oluşturun. Bunu genellikle normal olmayan alanlarla veya türetildiği modelle kaydetmeyi geçersiz kılarak yapıyorum:

# 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
Bu kesinlikle çalışmalıdır, ancak dahil olan fazladan DB alanı nedeniyle kabul edildi olarak işaretlenemez. Ayrıca sorgu kümesi satırının sonunda .count () eksikliğine dikkat edin.
mike_k

sayı () düzeltildi. Diğer tek çözüm (büyük katkıda bulunanlar.admin parçalarını alt sınıflara ayırmanın dışında) bir Jquery / Ajaxy hack'tir.
Andy Baker
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.