在Django中使用字段值的对数注释查询集

时间:2018-11-09 08:29:57

标签: python django database orm annotations

我研究了可用的django聚合函数,并注意到那里缺少对数。所以,我的问题是:如何在对Queryset的注释中使用对数?我无法在Queryset评估后取对数,因为我需要不完全用对数进行注释,而需要使用包含对数的表达式进行注释,例如对于某些模型UserTask,我需要注释{{1} }和User / F('task__cost')

UPD:如果不使用特定于数据库的函数(对于Postgres)也可以做到这一点,那是很棒的,但是这种解决方案也是可能的。

1 个答案:

答案 0 :(得分:1)

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

事实证明此功能在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'))