Django admin:如何按没有数据库字段的自定义list_display字段之一进行排序


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

如何根据客户对客户进行分类number_of_orders

admin_order_field属性不能在这里使用,因为它需要一个数据库字段进行排序。由于Django依赖底层数据库执行排序,这有可能吗?创建一个包含订单数量的汇总字段似乎在这里是过大的。

有趣的是:如果您在浏览器中手动更改url以在此列上进行排序-它将按预期工作!


“有趣的是:如果您在浏览器中手动更改url以在此列上进行排序-它将按预期工作!” 您的意思是:/ admin / myapp / customer /?ot = asc&o = 2您确定吗?
安迪·贝克

是的,无论是asc还是dsc。也许它只适用于小数。
mike_k 2010年

我认为它不能用于多个页面。
Chase Seibert'2

Answers:


159

我喜欢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'

这样,您只需在管理界面内进行注释。并非针对您执行的每个查询。


5
是的,这是一种更好的方法。:)
Greg 2012年

2
有一个建议编辑这个答案。我投票拒绝它,因为它删除了太多文本。我不了解Django,我不知道建议的代码更改是否值得一提。
吉尔斯(Gilles)'所以

1
@Gilles建议的编辑关于一个更简单的number_of_orders定义是正确的。这有效: def number_of_orders(self, obj): return obj.order__count
Nils

1
get_queryset()不是应该代替queryset()吗?
Mariusz Jamro '16

2
应该是get_queryset(self,request):...对于Django 1.6+
迈克尔

50

我尚未对此进行测试(我想知道它是否有效),但是如何定义一个自定义管理器,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 admin子类化!


1
与公认的答案相比,这是更好的答案。我遇到的问题是,当您在管理级别搜索某些内容以及更新后的查询集时,我会遇到这种问题,这会花费太多时间,并且会为找到的结果计数错误。
突变

0

我能想到的唯一方法是对字段进行非规范化。也就是说-创建一个实际字段,该字段将进行更新以与它源自的字段保持同步。我通常通过覆盖具有非规范化字段的模型或其衍生的模型来保存:

# 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
这当然应该可以,但是由于涉及额外的数据库字段,因此无法将其标记为接受。另外请注意,查询集行末尾缺少.count()。
mike_k'2

修复了count()。唯一的其他解决方案(缺少对contrib.admin的大块子类的继承)将是Jquery / Ajaxy hack。
安迪·贝克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.