django计算queryset中的特定行

时间:2016-07-12 11:56:23

标签: django split count django-queryset annotate

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

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

所以,目前我在views.py

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

在模板中:

第一张表:

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

另一张桌子:

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

这种方式有一些缺点,首先,现在我想计算有多少订单是“旧的”,在模板中显示这个数字。我不能,除非我做另一个查询。 可以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 %} 

1 个答案:

答案 0 :(得分:1)

您无法进行任何注释,因为您已拥有内存中的所有数据,因此无需进行.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

更新

关于您介绍的自定义管理器。我不认为你做得对,我想你想要的是这个:

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')。通过这种方式,您将只使用一个Query,这将在列表上保存一次迭代(因为Django将拆分列表只迭代一次),但是您在模板中的可读性会降低。