【问题标题】:Annotate Queryset with logarithm of field value in Django在 Django 中用字段值的对数注释查询集
【发布时间】:2018-11-09 08:29:56
【问题描述】:

我研究了可用的 django 聚合函数,发现那里缺少对数。所以,我的问题是:如何在注释查询集时使用对数?在 Queryset 评估后我不能取对数,因为我需要注释的不是对数,而是包含它的表达式,例如对于某些模型 UserTask 我需要用 F('task__cost') 注释 User / Log('task__solved_count')

UPD:如果我可以在不使用特定于数据库的函数(对于 Postgres)的情况下做到这一点,那就太好了,但这种解决方案也是可能的。

【问题讨论】:

标签: python django database orm annotations


【解决方案1】:

Django 有这些功能,这些已经添加在pull request 9622 [GitHub] 中。在开发分支中,这些已经存在于django.db.models.functions.math 模块下。但不在 版本中。 lists the source code 的 Django 开发文档中提供了一个页面。

原来这个功能在most popular database systems the same [Django ticket]上。您可以添加source code [GitHub]

from django.db.models import (
    DecimalField, FloatField, Func, IntegerField, Transform,
)
from django.db.models.functions import Cast

# ...

class DecimalInputMixin:

    def as_postgresql(self, compiler, connection, **extra_context):
        # Cast FloatField to DecimalField as PostgreSQL doesn't support the
        # following function signatures:
        # - LOG(double, double)
        # - MOD(double, double)
        output_field = DecimalField(decimal_places=sys.float_info.dig, max_digits=1000)
        clone = self.copy()
        clone.set_source_expressions([
            Cast(expression, output_field) if isinstance(expression.output_field, FloatField)
            else expression for expression in self.get_source_expressions()
        ])
        return clone.as_sql(compiler, connection, **extra_context)

class OutputFieldMixin:

    def _resolve_output_field(self):
        has_decimals = any(isinstance(s.output_field, DecimalField) for s in self.get_source_expressions())
        return DecimalField() if has_decimals else FloatField()

# ...

class Log(DecimalInputMixin, OutputFieldMixin, Func):
    function = 'LOG'
    arity = 2

    def as_sqlite(self, compiler, connection, **extra_context):
        if not getattr(connection.ops, 'spatialite', False):
            return self.as_sql(compiler, connection)
        # This function is usually Log(b, x) returning the logarithm of x to
        # the base b, but on SpatiaLite it's Log(x, b).
        clone = self.copy()
        clone.set_source_expressions(self.get_source_expressions()[::-1])
        return clone.as_sql(compiler, connection, **extra_context)

然后导入您定义的Log 函数,并像这样使用它:

User.objects.annotate(cost_solve_ratio=F('task__cost') / Log('task__solved_count'))

【讨论】:

  • 哇,太棒了!等待它成为主要 Django 包的一部分!
猜你喜欢
  • 2021-10-17
  • 1970-01-01
  • 1970-01-01
  • 2021-05-09
  • 2020-06-13
  • 1970-01-01
  • 2020-07-31
  • 2013-12-07
  • 2016-03-16
相关资源
最近更新 更多