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


332

모델을 쿼리합니다.

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있지만 패치하지 않고 어떻게 해야하는지 궁금합니다.

답변:


483

집계를 수행 하려는 경우 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}]

6
@Harry : 당신은 그것을 연결할 수 있습니다. 뭔가 같은 :Members.objects.filter(date=some_date).values('designation').annotate(dcount=Count('designation'))
엘리

57
질문이 있습니다.이 쿼리는 지정 및 dcount 만 반환합니다. 테이블의 다른 값도 얻으려면 어떻게해야합니까?
AJ

19
정렬이 지정 이외의 필드 인 경우 정렬을 재설정하지 않으면 작동하지 않습니다. stackoverflow.com/a/1341667/202137
Gidgidonihah

12
@Gidgidonihah True, 예제는 다음과 같아야합니다Members.objects.order_by('disignation').values('designation').annotate(dcount=Count('designation'))
bjunix

7
질문이 있습니다.이 쿼리는 지정 및 dcount 만 반환합니다. 테이블의 다른 값도 얻으려면 어떻게해야합니까?
Yann 叶

55

쉬운 해결책이지만 올바른 방법은 아닙니다. 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같은 결과를 얻을 수 있습니다.


order_by를 사용하여 방법을 알려주십시오.
simplyharsh

2
안녕하세요, 집계를 사용하지 않으면 order_by를 사용하여 group_by를 에뮬레이션하고 필요없는 항목을 제거 할 수 있습니다. 물론 이것은 에뮬레이션이며 많은 데이터를 사용하지 않는 경우에만 사용할 수 있습니다. 그가 집계에 대해 말하지 않았기 때문에 이것이 해결책이 될 수 있다고 생각했습니다.
Michael

이봐, 훌륭합니다-어떻게 작동하지 않는 execute_sql 사용법을 설명해 주시겠습니까?
rh0dium

8
Django 1.9에서는 더 이상 작동하지 않습니다. stackoverflow.com/questions/35558120/…
grokpot

1
이것은 ORM을 사용하는 일종의 해킹 방법입니다. 이전 쿼리 세트를 수동으로 전달하는 새 쿼리 세트를 인스턴스화하지 않아도됩니다.
이안 커크 패트릭

32

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태그로 전달하십시오 .


1
이것은 완벽 해요! 이 작업을 수행하는 간단한 방법을 많이 검색했습니다. 그리고 쿼리 세트에서도 작동합니다.
CarmenA

1
데이터베이스의 큰 데이터 집합을 읽은 다음 집계 된 값을 사용하면 이것은 완전히 잘못되었습니다.
Sławomir Lenart

@ SławomirLenart, 이것은 직선 DB 쿼리만큼 효율적이지 않을 수 있습니다. 그러나 간단한 사용 사례의 경우 좋은 해결책이 될 수 있습니다.
inostia

결과가 템플릿에 표시되면 작동합니다. 그러나 JsonResponse 또는 기타 간접 응답의 경우. 이 솔루션은 작동하지 않습니다.
Willy satrio nugroho

1
@Willysatrionugroho 예를 들어 stackoverflow.com/questions/477820/… 과 같이보기를 원한다면 도움이 될 수도 있습니다
inostia

7

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

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

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

추가 Manager 메소드 추가


1
왕복 솔루션의 종류. 내가 그것을 오랫동안 사용한다면 나는 그것을 사용했을 것입니다. 그러나 여기에는 지정 당 구성원 수가 필요합니다.
simplyharsh

문제 없어요. 1.1 집계 기능에 대해 언급했지만 릴리스 버전을 사용한다고 가정했습니다. :)
Van Gale

Django의 ORM의 약점을 보여주는 원시 쿼리를 사용하는 것이 전부입니다.
Sławomir Lenart

5

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

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

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


실제로 당신은 자유 그룹으로 제한 될뿐만 아니라, Django ORM 대신 SQLAlchemy를 시도하십시오.
Sławomir Lenart

5

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


django-group-byvalues방법 의 대안 입니다. 내가 생각하는 것은 다른 목적을위한 것입니다.
LShi

1
@LShi 물론 값에 대한 대안은 아닙니다. 이름은 ...로 표시 되는 반면 valuesSQL 은 SQL 입니다. 왜 공감해야합니까? 우리는 복잡한 문장 을 구현하기 위해 프로덕션에서 이러한 코드를 사용하고 있습니다 . selectgroup_bygroup bygroup_by
Risadinha

의사 는 말합니다group_by "대부분은 values ​​메소드와 유사하게 작동하지만 한 가지 차이점이 있습니다 ..."이 문서는 SQL에 대해 언급 GROUP BY하지 않으며 제공되는 사용 사례는 SQL 과 관련이 있다고 제안하지 않습니다 GROUP BY. 누군가가 이것을 분명히하면 다운 투표를 철회 할 것입니다. 그러나 그 의사는 실제로 오도합니다.
LShi

대한 문서를values 읽은 후 valuesGROUP BY처럼 작동 한다는 것을 놓쳤습니다 . 그것은 내 잘못이야. 나는 itertools.groupby이 장고 그룹 바이보다 values부족한 경우 사용하는 것이 더 간단하다고 생각합니다 .
LShi

1
데이터베이스에서 모든 것을 가져 오거나 가져 오지 않고 group by간단한 values호출로 위의 작업을 수행 할 수 없습니다 annotate. itertools.groupby작은 데이터 세트에 대한 작업 제안 이지만 페이지로 만들고 싶은 수천 개의 데이터 세트에는 적용되지 않습니다. 물론, 그 시점에서 준비된 (이미 그룹화 된) 데이터를 포함하는 특수 검색 인덱스를 고려해야합니다.
Risadinha

0

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

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()

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


-1

내가 착각하지 않으면 사용할 수 있습니다 .query -set .group_by = [ ' field ']


8
적어도 Django 1.6에서는 그렇지 않습니다. 'QuerySet'객체에는 'group_by'속성이 없습니다.
Facundo Olano

1
queryset.query.group_by = [...]를 올바르게 사용하면 쿼리의 의미가 깨지고 예상대로 작동하지 않습니다.
Luis Masuelli

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.