web-dev-qa-db-ja.com

Django admin:データベースフィールドを持たないカスタムlist_displayフィールドの1つでソートする方法

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

number_of_ordersに応じて、顧客をどのようにソートできますか?

admin_order_fieldプロパティは、並べ替えにデータベースフィールドが必要なため、ここでは使用できません。 Djangoは基礎となるDBに基づいてソートを実行しますか?注文数を含む集約フィールドを作成することは、ここではやり過ぎのようです。

おもしろいことに、ブラウザでURLを手動で変更してこの列をソートすると、期待どおりに機能します!

110
mike_k

私はこの問題に対するGregのソリューションが気に入りましたが、管理者で同じことを直接行えることを指摘したいと思います。

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'

この方法では、管理インターフェース内でのみ注釈を付けます。すべてのクエリを実行するわけではありません。

144
bbrik

私はこれをテストしていません(うまくいくかどうか知りたいです)が、集約された注文数を含むCustomerのカスタムマネージャーを定義し、admin_order_fieldその集合体、つまり

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'

編集:私はちょうどこのアイデアをテストし、完全に動作します-いいえDjango=管理サブクラス化が必要です!

47
Greg

私が考えることができる唯一の方法は、フィールドを非正規化することです。つまり、更新元のフィールドと同期を保つために更新される実際のフィールドを作成します。私は通常、非正規化されたフィールドまたはモデルの派生元のモデルでsaveをオーバーライドすることでこれを行います。

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