【问题标题】:Django admin: how to sort by one of the custom list_display fields that has no database fieldDjango admin:如何按没有数据库字段的自定义 list_display 字段之一进行排序
【发布时间】:2011-01-11 05:27:10
【问题描述】:
# 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 都有。也许它只适用于小数。
  • 我认为它不适用于多个页面。

标签: django sorting django-admin


【解决方案1】:

我能想到的唯一方法是对字段进行非规范化。也就是说 - 创建一个真实的字段,该字段得到更新以与其派生的字段保持同步。我通常通过覆盖具有非规范化字段的模型或其派生的模型的 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[...]

【讨论】:

  • 这当然应该有效,但由于涉及额外的数据库字段,因此无法将其标记为已接受。另请注意在查询集行末尾缺少 .count()。
  • 修复了 count()。唯一的其他解决方案(缺少对大块 contrib.admin 的子类化)将是 Jquery/Ajaxy hack。
【解决方案2】:

我还没有对此进行测试(我很想知道它是否有效)但是如何为 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 子类化!

【讨论】:

  • 与接受的答案相比,这是更好的答案。我在应用已接受的问题时遇到的问题是,当您在管理员级别搜索某些内容以及更新的查询集时,它会花费太多时间,而且它找到的结果计数错误。
【解决方案3】:

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

这样您只能在管理界面内进行注释。不是你做的每一个查询。

【讨论】:

  • 是的,这是一个更好的方法。 :)
  • 这个答案有一个suggested edit。我投票否决了它,因为它删除了太多文本。我不了解Django,我不知道建议的代码更改是否值得一提。
  • @Gilles 建议的编辑对于更简单的 number_of_orders 定义是正确的。这有效:def number_of_orders(self, obj): return obj.order__count
  • 不应该是get_queryset() 而不是queryset() 吗?
  • 应该是 get_queryset(self, request):... 对于 Django 1.6+
猜你喜欢
  • 1970-01-01
  • 2011-02-08
  • 1970-01-01
  • 2022-10-05
  • 1970-01-01
  • 2017-09-02
  • 2014-06-10
  • 2021-05-18
  • 2020-09-26
相关资源
最近更新 更多