programing

장고에서 카운트 주석을 위해 객체를 필터링하는 방법은 무엇입니까?

javamemo 2023. 6. 9. 21:42
반응형

장고에서 카운트 주석을 위해 객체를 필터링하는 방법은 무엇입니까?

간단한 모형을 .Event그리고.Participant:

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.

업데이트. 장고 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 3.x에서도 작동합니다.

Django 2.0+의 조건부 집계를 통해 과거에 발생했던 faff의 양을 더욱 줄일 수 있습니다.또한 Postgres'를 사용합니다.filter논리는 합 사례보다 다소 빠릅니다(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()
    )))

갱신하다

제가 언급한 서브쿼리 접근법은 이제 서브쿼리 표현식을 통해 장고 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)보다 이것을 선호합니다.

이전 버전의 경우 다음을 사용하여 동일한 작업을 수행할 수 있습니다.

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`"
})

저는 당신의 방법을 사용하는 것을 제안하고 싶습니다.Participant대신 쿼리 집합.

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

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

전체 예는 다음과 같습니다.

  1. 를 생성합니다.Events:

    event1 = Event.objects.create(title='event1')
    event2 = Event.objects.create(title='event2')
    
  2. 더하다Participant그들에게:

    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그들의 말에 의하면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. 그 다 버 에 킷 원 세 본 가 포 있 어 있 니 다 습 수 달 주 을 석 버 런 에 킷 음 이 한 로 므 러 으 트 되 함 ▁as 다 ▁they ▁buckets ▁you ▁those 니 ate ▁the ▁annot ▁of ▁contain ▁then 그 ▁can습 ▁set런있▁original 다 수 음달버킷원에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위의 방법으로부터

Django 3.x의 경우 주석 뒤에 필터를 쓰기만 하면 됩니다.

User.objects.values('user_id')
            .annotate(sudo_field=models.Count('likes'))
            .filter(sudo_field__gt=100)

위에sudo_field는 사용자 모델의 모델 필드가 아니며 여기서는 좋아요(또는 xyz)가 100개 이상인 사용자를 필터링하고 있습니다.

원하는 결과:

  • 보고서에 태스크가 추가된 사용자(할당된 사용자) - 전체 고유 사용자 수
  • 보고서에 추가된 태스크가 있지만 청구 가능성이 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}]>

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

반응형