[python] django에서 GROUP BY로 쿼리하는 방법은 무엇입니까?

모델을 쿼리합니다.

Members.objects.all()

그리고 그것은 반환합니다 :

Eric, Salesman, X-Shop
Freddie, Manager, X2-Shop
Teddy, Salesman, X2-Shop
Sean, Manager, X2-Shop

내가 원하는 것은 group_by다음과 같이 데이터베이스에 쿼리를 실행 하는 가장 좋은 Django 방법을 아는 것입니다.

Members.objects.all().group_by('designation')

물론 작동하지 않습니다. 나는 우리가 몇 가지 트릭을 할 수 있다는 것을 알고 django/db/models/query.py있지만 패치하지 않고 어떻게 해야하는지 궁금합니다.



답변

집계를 수행 하려는 경우 ORM집계 기능을 사용할 수 있습니다 .

from django.db.models import Count
Members.objects.values('designation').annotate(dcount=Count('designation'))

이로 인해 유사한 쿼리가 생성됩니다

SELECT designation, COUNT(designation) AS dcount
FROM members GROUP BY designation

출력 형태는

[{'designation': 'Salesman', 'dcount': 2},
 {'designation': 'Manager', 'dcount': 2}]


답변

쉬운 해결책이지만 올바른 방법은 아닙니다. raw SQL 을 사용하는 것입니다 .

results = Members.objects.raw('SELECT * FROM myapp_members GROUP BY designation')

또 다른 해결책은 group_by속성 을 사용하는 것입니다 .

query = Members.objects.all().query
query.group_by = ['designation']
results = QuerySet(query=query, model=Members)

이제 결과 변수를 반복하여 결과를 검색 할 수 있습니다. 참고group_by 문서화되지 않고 장고의 향후 버전에서 변경 될 수 있습니다.

그리고 … 왜 사용하고 싶 group_by습니까? 집계를 사용하지 않으면 order_by같은 결과를 얻을 수 있습니다.


답변

regroup템플릿 태그를 사용하여 속성별로 그룹화 할 수도 있습니다 . 문서에서 :

cities = [
    {'name': 'Mumbai', 'population': '19,000,000', 'country': 'India'},
    {'name': 'Calcutta', 'population': '15,000,000', 'country': 'India'},
    {'name': 'New York', 'population': '20,000,000', 'country': 'USA'},
    {'name': 'Chicago', 'population': '7,000,000', 'country': 'USA'},
    {'name': 'Tokyo', 'population': '33,000,000', 'country': 'Japan'},
]

...

{% regroup cities by country as country_list %}

<ul>
    {% for country in country_list %}
        <li>{{ country.grouper }}
            <ul>
            {% for city in country.list %}
                <li>{{ city.name }}: {{ city.population }}</li>
            {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

다음과 같습니다 :

  • 인도
    • 뭄바이 : 19,000,000
    • 캘커타 : 15,000,000
  • 미국
    • 뉴욕 : 20,000,000
    • 시카고 : 7,000,000
  • 일본
    • 도쿄 : 33,000,000

그것은 또한 QuerySet내가 믿는 것에서 작동합니다 .

출처 : https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

편집 : 사전 목록이 키 정렬되어 있지 않으면 예상대로 regroup태그 작동 하지 않습니다 . 반복적으로 작동합니다. 따라서 그룹화 키로 목록 (또는 쿼리 세트)을 정렬 한 다음 regroup태그로 전달하십시오 .


답변

이 스 니펫에 예시 된대로 사용자 지정 SQL을 수행해야합니다.

하위 쿼리를 통한 사용자 지정 SQL

또는 온라인 Django 문서에 표시된 사용자 지정 관리자에서

추가 Manager 메소드 추가


답변

Django는 무료 그룹 별 쿼리를 지원하지 않습니다 . 나는 그것을 아주 나쁜 방법으로 배웠다. ORM은 사용자 지정 SQL을 사용하지 않고 원하는 작업을 지원하도록 설계되지 않았습니다. 귀하는 다음으로 제한됩니다 :

  • RAW SQL (예 : MyModel.objects.raw ())
  • cr.execute 문장 (및 결과의 손으로 만든 파싱).
  • .annotate() (문별 그룹화는 .annotate ()의 하위 모델에서 행 _ 집계 = 횟수 ( ‘lines’) 집계와 같은 예에서 수행됨).

쿼리 세트를 통해 qs호출 할 수는 qs.query.group_by = ['field1', 'field2', ...]있지만 편집중인 쿼리를 모르고 쿼리 세트 개체의 내부를 손상시키지 않을 것이라는 보장이없는 경우 위험합니다. 또한 내부 (문서화되지 않은) API이므로 코드가 향후 장고 버전과 더 이상 호환되지 않을 위험없이 직접 액세스해서는 안됩니다.


답변

Django 모델을 그룹화하고 여전히 결과에서 QuerySet으로 작업 할 수있는 모듈이 있습니다 : https://github.com/kako-nawao/django-group-by

예를 들면 다음과 같습니다.

from django_group_by import GroupByMixin

class BookQuerySet(QuerySet, GroupByMixin):
    pass

class Book(Model):
    title = TextField(...)
    author = ForeignKey(User, ...)
    shop = ForeignKey(Shop, ...)
    price = DecimalField(...)

class GroupedBookListView(PaginationMixin, ListView):
    template_name = 'book/books.html'
    model = Book
    paginate_by = 100

    def get_queryset(self):
        return Book.objects.group_by('title', 'author').annotate(
            shop_count=Count('shop'), price_avg=Avg('price')).order_by(
            'name', 'author').distinct()

    def get_context_data(self, **kwargs):
        return super().get_context_data(total_count=self.get_queryset().count(), **kwargs)

‘book / books.html’

<ul>
{% for book in object_list %}
    <li>
        <h2>{{ book.title }}</td>
        <p>{{ book.author.last_name }}, {{ book.author.first_name }}</p>
        <p>{{ book.shop_count }}</p>
        <p>{{ book.price_avg }}</p>
    </li>
{% endfor %}
</ul>

annotate/ aggregate기본 Django 쿼리 와의 차이점 은 관련 필드의 속성을 사용한다는 것입니다 book.author.last_name.

함께 그룹화 된 인스턴스의 PK가 필요한 경우 다음 주석을 추가하십시오.

.annotate(pks=ArrayAgg('id'))

참고 : ArrayAggDjango 1.9 이상에서 사용할 수있는 Postgres 관련 기능입니다. https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg


답변

문서 에서는 값을 사용하여 쿼리 집합을 그룹화 할 수 있다고 말합니다.

class Travel(models.Model):
    interest = models.ForeignKey(Interest)
    user = models.ForeignKey(User)
    time = models.DateTimeField(auto_now_add=True)

# Find the travel and group by the interest:

>>> Travel.objects.values('interest').annotate(Count('user'))
<QuerySet [{'interest': 5, 'user__count': 2}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited for 2 times, 
# and the interest(id=6) had only been visited for 1 time.

>>> Travel.objects.values('interest').annotate(Count('user', distinct=True))
<QuerySet [{'interest': 5, 'user__count': 1}, {'interest': 6, 'user__count': 1}]>
# the interest(id=5) had been visited by only one person (but this person had 
#  visited the interest for 2 times

이 코드를 사용하여 모든 책을 찾아 이름별로 그룹화 할 수 있습니다.

Book.objects.values('name').annotate(Count('id')).order_by() # ensure you add the order_by()

여기 치트 시트를 볼 수 있습니다 .