Has*_*aig 53 python django django-queryset
使用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)
Run Code Online (Sandbox Code Playgroud)
我的问题是:如何获得所有发表的文章,按照过去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")
Run Code Online (Sandbox Code Playgroud)
这引发了错误:语法错误在"01"或附近(其中"01"是额外的日期时间对象).继续下去并不多.
Gwy*_*idD 100
使用条件聚合:
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(),
))
)
Run Code Online (Sandbox Code Playgroud)
说明:
通过您的文章的正常查询将使用numviews字段进行注释.该字段将被构造为CASE/WHEN表达式,由Count包装,对于读者匹配标准和NULL读者不匹配标准将返回1 .Count将忽略空值并仅计算值.
您将在最近未查看的文章上获得零,并且您可以使用该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"
Run Code Online (Sandbox Code Playgroud)
如果我们只想跟踪唯一的查询,我们可以添加区别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)
)
Run Code Online (Sandbox Code Playgroud)
这会产生:
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"
Run Code Online (Sandbox Code Playgroud)
您可以使用它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"')
Run Code Online (Sandbox Code Playgroud)
小智 26
对于django> = 2.0,您可以在聚合函数中使用带有filter参数的条件聚合:
from datetime import timedelta
from django.utils import timezone
from django.db.models import Count, Q # need import
Article.objects.annotate(
numviews=Count(
'readership__reader__id',
filter=Q(readership__what_time__gt=timezone.now() - timedelta(minutes=30)),
distinct=True
)
)
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
30128 次 |
| 最近记录: |