태그 보관물: django-aggregation

django-aggregation

Django에서 개수 주석에 대한 개체를 필터링하는 방법은 무엇입니까? 이벤트 를 쿼리해야합니다

간단한 장고 모델 고려 EventParticipant:

class Event(models.Model):
    title = models.CharField(max_length=100)

class Participant(models.Model):
    event = models.ForeignKey(Event, db_index=True)
    is_paid = models.BooleanField(default=False, db_index=True)

총 참가자 수로 이벤트 쿼리에 쉽게 주석을 달 수 있습니다.

events = Event.objects.all().annotate(participants=models.Count('participant'))

필터링 된 참가자 수로 주석을다는 방법은 is_paid=True무엇입니까?

참가자 수에 관계없이 모든 이벤트 를 쿼리해야합니다 . 예를 들어 주석이 달린 결과로 필터링 할 필요가 없습니다. 0참가자 가 있으면 괜찮습니다 0. 주석이 달린 값만 있으면됩니다.

문서예제는 여기에서 작동하지 않습니다 0. 으로 주석을 달지 않고 쿼리에서 개체를 제외하기 때문입니다 .

최신 정보. Django 1.8에는 새로운 조건식 기능 이 있으므로 이제 다음과 같이 할 수 있습니다.

events = Event.objects.all().annotate(paid_participants=models.Sum(
    models.Case(
        models.When(participant__is_paid=True, then=1),
        default=0,
        output_field=models.IntegerField()
    )))

업데이트 2. Django 2.0에는 새로운 조건부 집계 기능이 있습니다. 아래 에서 허용되는 답변을 참조하십시오.



답변

Django 2.0의 조건부 집계 를 사용하면 과거에 있었던 faff의 양을 더 줄일 수 있습니다. 이것은 또한 filter합계 사례보다 다소 빠른 Postgres의 논리를 사용할 것입니다 (20-30 %와 같은 숫자가 밴 디드 된 것을 보았습니다).

어쨌든, 귀하의 경우에는 다음과 같이 간단한 것을 찾고 있습니다.

from django.db.models import Q, Count
events = Event.objects.annotate(
    paid_participants=Count('participants', filter=Q(participants__is_paid=True))
)

주석 필터링에 대한 문서에는 별도의 섹션이 있습니다 . 조건부 집계와 동일하지만 위의 예와 더 비슷합니다. 어느 쪽이든, 이것은 내가 이전에했던 형편없는 하위 쿼리보다 훨씬 건강합니다.


답변

방금 Django 1.8에 새로운 조건식 기능 이 있다는 것을 알았 으므로 이제 다음과 같이 할 수 있습니다.

events = Event.objects.all().annotate(paid_participants=models.Sum(
    models.Case(
        models.When(participant__is_paid=True, then=1),
        default=0, output_field=models.IntegerField()
    )))


답변

최신 정보

내가 언급 한 하위 쿼리 접근 방식은 이제 subquery-expressions 를 통해 Django 1.11에서 지원됩니다 .

Event.objects.annotate(
    num_paid_participants=Subquery(
        Participant.objects.filter(
            is_paid=True,
            event=OuterRef('pk')
        ).values('event')
        .annotate(cnt=Count('pk'))
        .values('cnt'),
        output_field=models.IntegerField()
    )
)

최적화 (적절한 인덱싱 사용) 가 더 빠르고 쉬워야하므로 집계 (sum + case) 보다 이것을 선호합니다 .

이전 버전의 경우 다음을 사용하여 동일한 결과를 얻을 수 있습니다. .extra

Event.objects.extra(select={'num_paid_participants': "\
    SELECT COUNT(*) \
    FROM `myapp_participant` \
    WHERE `myapp_participant`.`is_paid` = 1 AND \
            `myapp_participant`.`event_id` = `myapp_event`.`id`"
})


답변

대신 쿼리 세트 의 .values방법 을 사용하는 것이 좋습니다 Participant.

간단히 말해서, 원하는 것은 다음과 같습니다.

Participant.objects\
    .filter(is_paid=True)\
    .values('event')\
    .distinct()\
    .annotate(models.Count('id'))

완전한 예는 다음과 같습니다.

  1. 2 Event초 만들기 :

    event1 = Event.objects.create(title='event1')
    event2 = Event.objects.create(title='event2')
  2. Participant그들에게 s를 추가하십시오 .

    part1l = [Participant.objects.create(event=event1, is_paid=((_%2) == 0))\
              for _ in range(10)]
    part2l = [Participant.objects.create(event=event2, is_paid=((_%2) == 0))\
              for _ in range(50)]
  3. 필드 Participant별로 모든 s 그룹화 event:

    Participant.objects.values('event')
    > <QuerySet [{'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 1}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, {'event': 2}, '...(remaining elements truncated)...']>

    여기에 뚜렷한 것이 필요합니다.

    Participant.objects.values('event').distinct()
    > <QuerySet [{'event': 1}, {'event': 2}]>

    여기서 .values하고 .distinct있는 것은 Participant요소별로 그룹화 된 두 개의 버킷을 만드는 것 event입니다. 해당 버킷에는 Participant.

  4. 그런 다음 원본 세트가 포함 된 버킷에 주석을 달 수 있습니다 Participant. 여기서 우리는의 수를 계산하려고합니다 Participant. 이것은 단순히 id해당 버킷에있는 요소 의 s를 계산하여 수행됩니다 (이니까 Participant).

    Participant.objects\
        .values('event')\
        .distinct()\
        .annotate(models.Count('id'))
    > <QuerySet [{'event': 1, 'id__count': 10}, {'event': 2, 'id__count': 50}]>
  5. 마지막으로 당신은 단지 원하는 Participant로모그래퍼 is_paid존재 True, 당신은 단지 이전 표현의 앞에 필터를 추가 할 수 있으며,이 위에 표시된 식을 얻을 :

    Participant.objects\
        .filter(is_paid=True)\
        .values('event')\
        .distinct()\
        .annotate(models.Count('id'))
    > <QuerySet [{'event': 1, 'id__count': 5}, {'event': 2, 'id__count': 25}]>

유일한 단점은 위의 방법 Event에서만 사용 하므로 나중에 검색해야한다는 것 id입니다.


답변

내가 찾고있는 결과 :

  • 보고서에 작업이 추가 된 사람 (담당자). -총 고유 인원수
  • 보고서에 작업이 추가되었지만 청구 가능성이 0보다 큰 작업에만 해당하는 사람.

일반적으로 두 가지 다른 쿼리를 사용해야합니다.

Task.objects.filter(billable_efforts__gt=0)
Task.objects.all()

하지만 하나의 쿼리에서 둘 다 원합니다. 그 후:

Task.objects.values('report__title').annotate(withMoreThanZero=Count('assignee', distinct=True, filter=Q(billable_efforts__gt=0))).annotate(totalUniqueAssignee=Count('assignee', distinct=True))

결과:

<QuerySet [{'report__title': 'TestReport', 'withMoreThanZero': 37, 'totalUniqueAssignee': 50}, {'report__title': 'Utilization_Report_April_2019', 'withMoreThanZero': 37, 'totalUniqueAssignee': 50}]>


답변