簡體   English   中英

Django ORM分組的子計數

[英]Django ORM grouped count of children

鑒於以下模型

class Parent(models.Model):
    name = models.CharField(max_length=100)

class Child(models.Model):
    parent = models.ForeignKey('Parent', related_name='children')
    status = models.CharField(max_length=10, choices=(('ok', 'ok'), ('fail', 'fail')))

我想在父模型/視圖上訪問父子的分組計數。

例如

parent.num_ok, parent.num_failed

要么

 parent.child_counts_per_status['ok']

需要在SQL中完成計數,因為為所有父母加載所有子代,然后在內存中對其進行計數是非常大的開銷(每個父代可能有成千上萬的子代)

如果要在ORM之外編寫此代碼,則會執行以下操作:

select parent.id, parent.name, child.status, count(*) from parent
inner join child on child.parent_id = parent.id
group by parent.id, parent.name, child.status

但是,由於我將限制父母的數量(通過分頁),因此可以:

select parent.* from parent where ... (page is)

然后每個父母執行一次:

select status, count(*) from child where parent_id = :parent_id
group by status

通過Django ORM是否可以使用這些選項之一?

同樣如果是..如何將其插入對象模型? 我正在使用Django Rest Framework,並且我猜查詢將進入到views.py中,它目前看起來像:

class ParentViewSet(viewsets.ModelViewSet):

    queryset = Parent.objects.all()

如果要獲取特定父母(例如parent1)的“正常”孩子數,請使用

parent1.children.filter(status='ok').count()

如果您需要為所有父母計算好孩子的數目,則可以使用注釋 ,例如,要打印每個父母的孩子數目,您將使用

from django.db.models import Count 

parents = Parent.objects.filter(children__status='ok').annotate(c_count=Count('children'))

for p in parents:
  print p.c_count

您將分別使用queryset

Parent.objects.filter(children__status='ok').distinct()

(我們使用distinct來消除重復)

下面將按照您的建議,在名為num_oknum_fail的屬性num_ok兩種類型的子代的計數注釋每個父對象。

這在內部創建的SQL幾乎與您建議的SQL完全相同,從而使計數工作由數據庫承擔,而不是在Python和Django中完成。

from django.db.models import Count, Case, When, IntegerField

...

queryset = Parent.objects.annotate(
               num_ok=Count(Case(
                            When(children__status='ok', then=1),
                                output_field=IntegerField()))
           ).annotate(
               num_fail=Count(Case(
                              When(children__status='fail', then=1),
                                output_field=IntegerField())))

這將允許您遍歷Parent對象並按以下方式檢索計數:

for parent in queryset:
    print(parent.num_ok)
    print(parent.num_fail)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM