【问题标题】:Django aggregate multiple columns after arithmetic operation算术运算后Django聚合多列
【发布时间】:2012-12-04 18:33:29
【问题描述】:

我对 Django 1.4.4 有一个非常奇怪的问题。

我有这个模型:

class LogQuarter(models.Model):
  timestamp = models.DateTimeField()
  domain = models.CharField(max_length=253)
  attempts = models.IntegerField()
  success = models.IntegerField()
  queue = models.IntegerField()
  ...

我需要收集前 20 个已发送属性较高的域。发送的属性是尝试 - 队列。

这是我的要求:

obj = LogQuarter.objects\
      .aggregate(Sum(F('attempts')-F('queue')))\
      .values('domain')\
      .filter(**kwargs)\
      .order_by('-sent')[:20]

我也尝试了额外的,但它不起作用。

这真的是基本的 SQL,我很惊讶 Django 不能这样做。

有人有解决办法吗?

【问题讨论】:

    标签: django django-models


    【解决方案1】:

    您实际上可以通过子类化一些聚合功能来做到这一点。这需要深入研究代码才能真正理解,但这是我为MAXMIN 编写的代码。 (注:此代码基于 Django 1.4 / MySQL)。

    首先继承底层聚合类并覆盖 as_sql 方法。此方法将实际 SQL 写入数据库查询。我们必须确保正确引用传入的字段并将其与正确的表名相关联。

    from django.db.models.sql import aggregates
    class SqlCalculatedSum(aggregates.Aggregate):
      sql_function = 'SUM'
      sql_template = '%(function)s(%(field)s - %(other_field)s)'
    
      def as_sql(self, qn, connection):
        # self.col is currently a tuple, where the first item is the table name and
        # the second item is the primary column name. Assuming our calculation is
        # on two fields in the same table, we can use that to our advantage. qn is
        # underlying DB quoting object and quotes things appropriately. The column
        # entry in the self.extra var is the actual database column name for the
        # secondary column.
        self.extra['other_field'] = '.'.join(
            [qn(c) for c in (self.col[0], self.extra['column'])])
        return super(SqlCalculatedSum, self).as_sql(qn, connection)
    

    接下来,子类化通用模型聚合类并覆盖 add_to_query 方法。此方法决定了如何将聚合添加到底层查询对象。我们希望能够传入字段名称(例如queue)但获取相应的数据库列名称(以防它不同)。

    from django.db import models
    class CalculatedSum(models.Aggregate):
      name = SqlCalculatedSum
    
      def add_to_query(self, query, alias, col, source, is_summary):
        # Utilize the fact that self.extra is set to all of the extra kwargs passed
        # in on initialization. We want to get the corresponding database column
        # name for whatever field we pass in to the "variable" kwarg.
        self.extra['column'] = query.model._meta.get_field(
            self.extra['variable']).db_column
        query.aggregates[alias] = self.name(
            col, source=source, is_summary=is_summary, **self.extra)
    

    然后您可以在这样的注释中使用您的新类:

    queryset.annotate(calc_attempts=CalculatedSum('attempts', variable='queue'))
    

    假设您的 attemptsqueue 字段具有相同的 db 列名称,这应该会生成类似于以下内容的 SQL:

    SELECT SUM(`LogQuarter`.`attempts` - `LogQuarter`.`queue`) AS calc_attempts
    

    你去吧。

    【讨论】:

    • 非常感谢,我想到那时我可以找到更简单的东西,但它可以完成工作!
    【解决方案2】:

    我不确定您是否可以这样做Sum(F('attempts')-F('queue'))。它应该首先抛出一个错误。我想,更简单的方法是使用额外的。

    result = LogQuarter.objects.extra(select={'sent':'(attempts-queue)'}, order_by=['-sent'])[:20]
    

    【讨论】:

    • 这个解决方案的问题是我在域上没有 group_by。我收集了前 20 行,但没有汇总。
    猜你喜欢
    • 2017-04-28
    • 2015-07-14
    • 1970-01-01
    • 2014-04-26
    • 1970-01-01
    • 2018-10-16
    • 2017-11-19
    • 2016-03-10
    • 1970-01-01
    相关资源
    最近更新 更多