Django のクエリセットで Count を条件付きでアノテートする方法
質問
Django の ORM を使って、次のようなことができますか?
queryset.objects.annotate(Count('queryset_objects', gte=VALUE))
. 私のドリフトをキャッチ?
考えられる答えを説明するために使用する簡単な例です。
Django ウェブサイトでは、コンテンツ作成者は記事を投稿し、一般ユーザはその記事を見る (つまり読む) ことができます。記事は公開 (すなわち、すべての人が読むことができる) か、下書きモードかのどちらかになります。これらの要件を表すモデルは以下の通りです。
class Article(models.Model):
author = models.ForeignKey(User)
published = models.BooleanField(default=False)
class Readership(models.Model):
reader = models.ForeignKey(User)
which_article = models.ForeignKey(Article)
what_time = models.DateTimeField(auto_now_add=True)
質問です。 どのように私は過去30分からのユニークな読者によって並べられたすべての公開記事を取得することができますか?すなわち、私は、各公開記事が過去30分間にどれだけの明確な(ユニークな)ビューを得たかをカウントし、次にこれらの明確なビューによって並べられた記事のリストを作成したいです。
私は試してみました。
date = datetime.now()-timedelta(minutes=30)
articles = Article.objects.filter(published=True).extra(select = {
"views" : """
SELECT COUNT(*)
FROM myapp_readership
JOIN myapp_article on myapp_readership.which_article_id = myapp_article.id
WHERE myapp_readership.reader_id = myapp_user.id
AND myapp_readership.what_time > %s """ % date,
}).order_by("-views")
これがエラーを生んだ。 またはその付近で構文エラーが発生しました。 (ここで "01" は extra 内の datetime オブジェクトでした)。大したことではありません。
どのように解決するのですか?
djangoの場合 >= 1.8
使用方法 条件付き集計 :
from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=IntegerField(),
))
)
説明
通常のクエリの場合、あなたの記事には
numviews
フィールドがあります。このフィールドは CASE/WHEN 式で構成され、Count でラップされ、条件にマッチする読者数に対して 1 を返し、そして
NULL
を返します。CountはNULLを無視し、値のみをカウントします。
最近閲覧されていない記事には0が表示されるので、それを利用することができます。
numviews
フィールドをソートとフィルタリングに使用できます。
PostgreSQLのためのこの背後にあるクエリは、次のようになります。
SELECT
"app_article"."id",
"app_article"."author",
"app_article"."published",
COUNT(
CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN 1
ELSE NULL END
) as "numviews"
FROM "app_article" LEFT OUTER JOIN "app_readership"
ON ("app_article"."id" = "app_readership"."which_article_id")
GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"
ユニークなクエリだけを追跡したい場合は、区別のために
Count
に追加し
When
節に値を返させ、明瞭にしたい。
from django.db.models import Count, Case, When, CharField, F
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=F('readership__reader')), # it can be also `readership__reader_id`, it doesn't matter
output_field=CharField(),
), distinct=True)
)
これで生成されます。
SELECT
"app_article"."id",
"app_article"."author",
"app_article"."published",
COUNT(
DISTINCT CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN "app_readership"."reader_id"
ELSE NULL END
) as "numviews"
FROM "app_article" LEFT OUTER JOIN "app_readership"
ON ("app_article"."id" = "app_readership"."which_article_id")
GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"
django < 1.8 と PostgreSQL の場合
を使えばいいだけです。
raw
を使えば、新しいバージョンの django が作成した SQL 文を実行することができます。を使わずにそのデータを照会するための、単純で最適化された方法はないようです。
raw
を使わずに (たとえ
extra
を使用した場合でも、必要な
JOIN
節が必要です)。
Articles.objects.raw('SELECT'
' "app_article"."id",'
' "app_article"."author",'
' "app_article"."published",'
' COUNT('
' DISTINCT CASE WHEN "app_readership"."what_time" < 2015-11-18 11:04:00.000000+01:00 THEN "app_readership"."reader_id"'
' ELSE NULL END'
' ) as "numviews"'
'FROM "app_article" LEFT OUTER JOIN "app_readership"'
' ON ("app_article"."id" = "app_readership"."which_article_id")'
'GROUP BY "app_article"."id", "app_article"."author", "app_article"."published"')
関連
-
[解決済み】Djangoのクエリセットフィルタリングでnot equalを行うにはどうすればよいですか?
-
[解決済み] pipでPythonの全パッケージをアップグレードする方法
-
[解決済み] リスト項目の出現回数を数えるにはどうしたらいいですか?
-
[解決済み] Pandas DataFrameの行数を取得する方法は?
-
[解決済み] Django のビューで 2 つ以上のクエリセットを結合するにはどうすればよいですか?
-
[解決済み] django のクエリセットで OR 条件を実行する方法は?
-
[解決済み] Pythonの要素別タプル演算(sumなど
-
[解決済み] 文字列から先頭と末尾のスペースを削除するには?
-
[解決済み] Django 1.7で初期マイグレーションからマイグレートバックする方法は?
-
[解決済み] PyQtアプリケーションのスレッド化。QtスレッドとPythonスレッドのどちらを使うか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] PythonでファイルのMD5チェックサムを計算するには?重複
-
[解決済み] 小数点以下1桁を取得する[重複]。
-
[解決済み] タプルのリストを複数のリストに変換するには?
-
[解決済み] Python 2.7サポート終了?
-
[解決済み] 値で列挙名を取得する [重複]。
-
[解決済み] SQLAlchemy - テーブルのリストを取得する
-
[解決済み] オブジェクトのリストに特定の属性値を持つオブジェクトが含まれているかどうかをチェックする
-
[解決済み] 異なる順序で同じ要素を持つ2つのJSONオブジェクトを等しく比較するには?
-
[解決済み] Python Empty Generator 関数
-
[解決済み] Django filter queryset __in for *every* item in list