【问题标题】:How do I get my Django query to only return an average and another column?如何让我的 Django 查询只返回平均值和另一列?
【发布时间】:2019-04-10 17:00:06
【问题描述】:

我正在使用 Django 和 Python 3.7。我有这些模型...

class Article(models.Model):
    website = models.ForeignKey(Website, on_delete=models.CASCADE, related_name='articlesite')
    title = models.TextField(default='', null=False)
    path = models.TextField(default='', null=False)
    url = models.TextField(default='', null=False)
    created_on = models.DateTimeField(db_index=True, default=datetime.now)


class ArticleStat(models.Model):
    objects = ArticleStatManager()
    article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='articlestats')
    elapsed_time_in_seconds = models.IntegerField(default=0, null=False)
    hits = models.FloatField(default=0, null=False)

我想编写一个 Django 查询,它返回两列数据——一个 elapsed_time_in_seconds(来自上面的 ArticleStat 模型)和这个特定时间间隔的平均点击次数。混合中还有其他一些限制。我不知道该怎么做。我尝试了以下

qset = ArticleStat.objects.annotate(avg_score=Avg(F("hits")),
                                    hour=(Func(
                                            Func(
                                                F("article__created_on"), function='HOUR FROM'),
                                            function='EXTRACT'))).filter(
    article__website=website,
    hour=hour)

但这会导致错误“TypeError: expected string or bytes-like object”。我对如何指定我的 SQL 语句的“GROUP BY”部分感到困惑,然后我对如何返回我想要的两列数据感到困惑,因为我认为上面只返回 ArticleStat 对象,这不会t 包括我的平均水平。

【问题讨论】:

  • 如果您对对象进行了注释,则这些对象确实包括平均值。如果您不需要对象而只需要两个字段,请使用values()
  • 哦,好的,如果它还包含我想要的新字段,我可以拥有该对象。但是,为什么我在上面得到“TypeError: expected string or bytes-like object”错误?

标签: python django python-3.x aggregate average


【解决方案1】:

你需要在hour注解上设置一个输出字段,否则Django不知道你已经将原始日期类型(created_on)转换为数字类型(hour),并期望通过一个日期字符串(或一个日期对象,但令人困惑的是它没有说出来)。如果您将日期作为hour 传递给filter(),您会看到TypeError 消失(但您会从数据库中获得ProgrammingError)。

既然您将小时提取为整数,那么使用IntegerField 怎么样:

from django.db.models import IntegerField

qset = ArticleStat.objects.annotate(
    avg_score=Avg(F("hits")),
    hour=(Func(
            Func(F("article__created_on"), function='HOUR FROM'),
            function='EXTRACT',
            output_field=IntegerField()
    ))
)

【讨论】:

  • 谢谢。因此,根据您的评论,为了强制仅使用“elapsed_time_in_seconds”的表达式进行分组,我添加了值('elapsed_time_in_seconds'),但即便如此,生成的 GROUP BY sql 包括 taht 和 'EXTRACT(HOUR FROM("article"." created_on"'。如何强制 GROUP BY 仅包含一列?
  • 将注释分开,因为它们有不同的用途。一个用于过滤,另一个用于聚合。首先注释hour,然后过滤hour,然后只使用您要分组的值执行values,然后使用所需的聚合进行注释。如果它不起作用,请在问题中发布更新后的查询。
猜你喜欢
  • 2020-05-25
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 2013-05-22
  • 2018-01-20
  • 1970-01-01
  • 2012-12-08
  • 1970-01-01
相关资源
最近更新 更多