Programming

Django에서 개수 주석에 대한 개체를 필터링하는 방법은 무엇입니까?

procodes 2020. 8. 7. 19:16
반응형

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}]>
    

    What .values and .distinct are doing here is that they are creating two buckets of Participants grouped by their element event. Note that those buckets contain Participant.

  4. You can then annotate those buckets as they contain the set of original Participant. Here we want to count the number of Participant, this is simply done by counting the ids of the elements in those buckets (since those are Participant):

    Participant.objects\
        .values('event')\
        .distinct()\
        .annotate(models.Count('id'))
    > <QuerySet [{'event': 1, 'id__count': 10}, {'event': 2, 'id__count': 50}]>
    
  5. Finally you want only Participant with a is_paid being True, you may just add a filter in front of the previous expression, and this yield the expression shown above:

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

The only drawback is that you have to retrieve the Event afterwards as you only have the id from the method above.


What result I am looking for:

  • People (assignee) who have tasks added to a report. - Total Unique count of People
  • People who have tasks added to a report but, for task whoe bill ability is more than 0 only.

In general, I would have to use two different queries:

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

But I want both in one query. Hence:

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

Result:

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

참고URL : https://stackoverflow.com/questions/30752268/how-to-filter-objects-for-count-annotation-in-django

반응형