django 计算查询集中的特定行

django count specific rows in queryset

class Order(models.Model):
   name = models.CharField(max_length=100)
   # other fields..
   user = models.ForeginKey(User)
   old = models.BooleanField(default=False)

我想显示特定用户的所有订单,但我想将它们拆分为 "old" 和非 "old"。

所以,目前我在 views.py:

orders = Order.objects.filter(user=user)

在模板中:

第一个table:

<table>
{% for order in orders %}
{% if not order.old %}
   <tr>
     <td>... </td> 
   </tr>
{% endif %}
{% endfor %}
</table>

另一个table:

{% for order in orders %}
{% if order.old %}
   <tr>
     <td>...</td>
   <tr>
{% endif %}
{% endfor %}

这种方式有一些缺点,首先,现在我想统计有多少订单是"old",以便在模板中显示这个数字。我不能,除非我再做一次查询。 可以annotate(number_of_old=Count('old'))吗?或者我必须再做一次查询?

那最好的是什么?
1. 做两个查询,一个使用 old=False,另一个使用 old=True,并将两个查询集传递给模板。并在查询集上使用 |len 过滤器
2. 做一个这样的查询并以某种方式在 python 中拆分它们?这将不太方便,因为我有一个类似的结构,我想像那样拆分。

我是否应该调用 DB .count()?

编辑:
如果我这样写我的模型:

class Order(models.Model):
   name = models.CharField(max_length=100)
   # other fields..
   user = models.ForeginKey(User)
   old = models.BooleanField(default=False)
   objects = CustomManager() # Custom manager


class CustomQueryset(models.QuerySet):
    def no_old(self):
        return self.filter(old=False)

class CustomManager(models.Manager):
    def get_queryset(self):
        return CustomQuerySet(model=self.model, using=self._db)

此模板代码产生一个或两个查询吗?

{% if orders.no_old %}
{% for order orders.no_old %}
... 
{% endfor %}
{% endif %} 

你不能做任何注释,也没有必要做.count(),因为你已经把所有的数据都放在内存中了。所以它真的介于:

orders = Order.objects.filter(user=user)
old_orders = [o for o in orders if o.old]
new_orders = [o for o in orders if not o.old]

#or

old_orders = Order.objects.filter(user=user, old=True)
new_orders = Order.objects.filter(user=user, old=False)

在这种特定情况下,我认为不会有任何性能差异。对于这两个查询,我个人会选择第二种方法。

一本关于问题的好书:Django Database access optimization

更新

关于您介绍的自定义Manager。我认为你做的不正确我认为你想要的是这个:

class CustomQueryset(models.QuerySet):
    def no_old(self):
        return self.filter(old=False)

class Order(models.Model):
   name = models.CharField(max_length=100)
   # other fields..
   user = models.ForeginKey(User)
   old = models.BooleanField(default=False)

   #if you already have a manager
   #objects = CustomManager.from_queryset(CustomQueryset)()
   #if you dont:
   objects = CustomQueryset.as_manager()

所以有:

orders = Order.objects.filter(user=user)

如果你这样做 {% if orders.no_old %} 将执行另一个查询,因为 this is new QuerySet 没有缓存的实例..

关于 {% regroup %} 标签

正如您所说,要使用它,您需要 .order_by('old'),如果您有其他订单,您仍然可以使用它,只需在 old 之后应用您的订单即可,例如.order_by('old', 'another_field')。这样您将只使用一个查询,这将节省您对列表的一次迭代(因为 Django 将拆分列表只迭代一次),但模板的可读性会降低。