代码之家  ›  专栏  ›  技术社区  ›  pomo_mondreganto

用Django中字段值的对数注释Queryset

  •  2
  • pomo_mondreganto  · 技术社区  · 6 年前

    User Task 我需要注释 具有 F('task__cost') Log('task__solved_count') .

    UPD:如果不使用特定于数据库的函数(对于Postgres),那就太好了,但这种解决方案也是可能的。

    1 回复  |  直到 6 年前
        1
  •  1
  •   willeM_ Van Onsem    6 年前

    Django有这些函数,这些函数已经添加到 pull request 9622 [GitHub] django.db.models.functions.math 模块。但不是在 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'))