web-dev-qa-db-ja.com

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でいくつかのトリックを実行できることはわかっていますが、パッチを適用せずにそれを実行する方法を知りたいと思っているだけです。

279
simplyharsh

集約するつもりなら、 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}]
413
Guðmundur H

簡単な解決策ですが、正しい方法ではありません--- 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は文書化されておらず、Djangoの将来のバージョンで変更されるかもしれないことに注意してください。

そして、...なぜgroup_byを使いたいのですか?集計を使用しない場合は、order_byを使用して同様の結果を得ることができます。

43
Michael

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
  • 米国
    • ニューヨーク:2000万
    • シカゴ:700万
  • 日本
    • 東京:33,000,000

それは私が信じているQuerySetsでも動作します。

ソース: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup

13
inostia

このスニペットに例示されているように、カスタムSQLを実行する必要があります。

副問い合わせによるカスタムSQL

あるいはオンラインのDjangoのドキュメントにあるようなカスタムマネージャで:

追加のManagerメソッドを追加する

5
Van Gale

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>

基本的なDjangoクエリの_ annotate/_ aggregateとの違いは、関連フィールドの属性を使うことです。 book.author.last_name

グループ化されたインスタンスのPKが必要な場合は、次の注釈を追加してください。

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

注:ArrayAggはDjango 1.9以降で利用可能なPostgres固有の関数です。 https://docs.djangoproject.com/en/1.10/ref/contrib/postgres/aggregates/#arrayagg

4
Risadinha

Djangoはクエリによるフリーグループをサポートしません。私はそれを非常に悪い方法で学んだ。 ORMは、カスタムSQLを使用せずに、あなたがやりたいことのようなものをサポートするようには設計されていません。あなたは制限されています:

  • RAW SQL(すなわちMyModel.objects.raw())
  • cr.execute文(および結果の手作り解析).
  • .annotate()(lines_count = Count( 'lines')の集約のような例では、文によるグループ化は.annotate()の子モデルで実行されます)。

クエリセットqsを介してqs.query.group_by = ['field1', 'field2', ...]を呼び出すことができますが、編集しているクエリがわからず、それが機能し、QuerySetオブジェクトの内部構造を破壊しないという保証がない場合は危険です。そのうえ、それは将来のDjangoバージョンともはや互換性がないコードを危険にさらすことなしに直接アクセスするべきではない内部(文書化されていない)APIです。

3
Luis Masuelli

document は、クエリセットをグループ化するために値を使用できると言っています。

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

あなたはいくつかのチートシートを見ることができます ここ

0
ramwin