Djangoのcountおよびgroup byと同等


91

次のようなモデルがあります。

class Category(models.Model):
    name = models.CharField(max_length=60)

class Item(models.Model):
    name = models.CharField(max_length=60)
    category = models.ForeignKey(Category)

各カテゴリのアイテムの選択数(カウントのみ)が欲しいので、SQLでは次のように簡単です。

select category_id, count(id) from item group by category_id

これを「Djangoの方法」で行うことと同等のものはありますか?それともプレーンSQLが唯一の選択肢ですか?私はDjangoのcount()メソッドに精通していますが、group byがどのように適合するかわかりません。



@CiroSantilli巴撤馬文件六四事件法轮功これはどのように複製されていますか?この質問は2008年に行われたもので、あなたが言及しているのは2年後のことです。
セルゲイゴロフチェンコ2016年

現在のコンセンサスは、「品質」で閉じることです。< meta.stackexchange.com/questions/147643/... >「品質」は測定できないので、私はちょうどupvotesで行きます。;-)おそらく、どの質問がタイトルの最も優れた初心者向けGoogleキーワードにヒットするかということになります。
Ciro Santilli郝海东冠状病六四事件法轮功

回答:


131

ここで、今発見したように、Django 1.1集約APIでこれを行う方法です。

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))

3
Djangoの他のほとんどのものと同じように、これを見るのはまったく意味がありませんが、(Djangoの他のほとんどのものとは異なり)実際に試してみると、それは
素晴らしかった

3
がデフォルトの順序ではないorder_by()場合に使用する必要があることに注意してください'category'。(ダニエルのより包括的な回答を参照してください。)
Rick Westera

これが機能する理由は、「の.annotate()後に動作が若干異なる.values()ためです。ただし、values()句を使用して結果セットで返される列を制約する場合、注釈を評価する方法がわずかに異なります。元のクエリセットの各結果の結果、元の結果は、values()句で指定されたフィールドの一意の組み合わせに従ってグループ化されます。
mgalgs 2018

58

更新:ORMの完全なサポートがDjango 1.1に含まれるようになりました。プライベートAPIの使用に関する以下の警告に忠実に、ここに記載されている方法は、Djangoの1.1以降のバージョンでは機能しません。理由を理解していません。 1.1以降を使用している場合は、とにかく実際の集約APIを使用する必要があります。

コア集約のサポートは1.0にすでに含まれていました。ドキュメント化されておらず、サポートもされておらず、フレンドリーなAPIもまだありません。しかし、これが1.1が到着するまでの間に使用する方法です(自己責任で、query.group_by属性はパブリックAPIの一部ではなく、変更される可能性があることを十分に理解しています)。

query_set = Item.objects.extra(select={'count': 'count(1)'}, 
                               order_by=['-count']).values('count', 'category')
query_set.query.group_by = ['category_id']

次にquery_setを反復処理すると、返される各値は「カテゴリ」キーと「カウント」キーを持つディクショナリになります。

ここでは、-countで順序付けする必要はありません。これは、それがどのように行われるかを示すために含まれているだけです(クエリセット構築チェーンの他の場所ではなく、.extra()呼び出しで行う必要があります)。また、count(1)の代わりにcount(id)と言うこともできますが、後者の方が効率的です。

.query.group_byを設定する場合、値はDjangoフィールド名( 'category')ではなく、実際のDB列名( 'category_id')でなければならないことにも注意してください。これは、すべてがDjango用語ではなく、DB用語であるレベルでクエリの内部を調整しているためです。


古いメソッドの+1。現在サポートされていない場合でも、控えめに言っても賢明です。すごい、本当に。
2011年

docs.djangoproject.com/en/dev/topics/db/aggregation/…にあるDjango集約APIをご覧ください。他の複雑なタスクを実行できます。強力な例がいくつかあります。
serfer2 14

@ serfer2はい、それらのドキュメントはこの回答の上からすでにリンクされています。
Carl Meyer、

56

Django 1.1でのグループ化がどのように機能するかについて少し混乱していたので、これをどのように使用するかについて、ここで詳しく説明すると思いました。まず、マイケルが言ったことを繰り返すには:

ここで、今発見したように、Django 1.1集約APIでこれを行う方法です。

from django.db.models import Count
theanswer = Item.objects.values('category').annotate(Count('category'))

あなたがする必要があることにも注意してくださいfrom django.db.models import Count

これにより、カテゴリのみが選択され、というアノテーションが追加されcategory__countます。デフォルトの順序によっては、これで十分な場合もありますが、デフォルトの順序でcategoryこれ以外のフィールドを使用すると機能しません。この理由は、注文に必要なフィールドも選択され、各行が一意になるため、必要な方法で項目がグループ化されないためです。これを修正する簡単な方法の1つは、順序をリセットすることです。

Item.objects.values('category').annotate(Count('category')).order_by()

これにより、希望どおりの結果が得られます。使用できる注釈の名前を設定するには:

...annotate(mycount = Count('category'))...

次にmycount、結果で呼び出される注釈があります。

グループ化に関する他のすべては、私には非常に簡単でした。詳細については、Django集計APIを確認してください。


1
外部キーフィールドItem.objects.values( 'category__category')上のアクションの同じセットを実行する注釈() 'category__category'(カウント)ORDER_BY()します。。
ミュータント

デフォルトの順序フィールドがどのように決定されるのですか?
Bogatyr

2

これどう?(遅い以外)

counts= [ (c, Item.filter( category=c.id ).count()) for c in Category.objects.all() ]

多くの行をフェッチする場合でも、短いという利点があります。


編集。

1つのクエリバージョン。ところで、これは多くの場合、データベースのSELECT COUNT(*)より高速です。見てみてください。

counts = defaultdict(int)
for i in Item.objects.all():
    counts[i.category] += 1

それは素晴らしくて短いですが、私はカテゴリーごとに別々のデータベース呼び出しを持つことを避けたいです。
セルゲイゴロフチェンコ

これは、単純なケースには本当に良いアプローチです。大きなデータセットがあり、不要な大量のデータを引き出すことなく、カウントに応じて順序+制限(つまり、ページ分割)を行う場合に、この値は低下します。
カールマイヤー

@Carl Meyer:True-大規模なデータセットの場合、わからないことがあります。ただし、それを確実にするためにベンチマークを行う必要があります。また、サポートされていないものにも依存しません。サポートされていない機能がサポートされるまで、暫定的に機能します。
S.Lott、2008
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.