モデルを問い合わせます。
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
は文書化されておらず、Djangoの将来のバージョンで変更されるかもしれないことに注意してください。
そして、...なぜ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>
こんな感じです:
それは私が信じているQuerySet
sでも動作します。
ソース: https://docs.djangoproject.com/en/2.1/ref/templates/builtins/#regroup
このスニペットに例示されているように、カスタムSQLを実行する必要があります。
あるいはオンラインのDjangoのドキュメントにあるようなカスタムマネージャで:
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
Djangoはクエリによるフリーグループをサポートしません。私はそれを非常に悪い方法で学んだ。 ORMは、カスタムSQLを使用せずに、あなたがやりたいことのようなものをサポートするようには設計されていません。あなたは制限されています:
cr.execute
文(および結果の手作り解析)..annotate()
(lines_count = Count( 'lines')の集約のような例では、文によるグループ化は.annotate()の子モデルで実行されます)。クエリセットqs
を介してqs.query.group_by = ['field1', 'field2', ...]
を呼び出すことができますが、編集しているクエリがわからず、それが機能し、QuerySetオブジェクトの内部構造を破壊しないという保証がない場合は危険です。そのうえ、それは将来のDjangoバージョンともはや互換性がないコードを危険にさらすことなしに直接アクセスするべきではない内部(文書化されていない)APIです。
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()
あなたはいくつかのチートシートを見ることができます ここ 。