【问题标题】:Making queries using F() and timedelta at django在 django 使用 F() 和 timedelta 进行查询
【发布时间】:2011-09-03 18:29:49
【问题描述】:

我有以下型号:

class Process(models.Model):
  title = models.Charfield(max_length=255)
  date_up = models.DateTimeField(auto_now_add=True)
  days_activation = models.PositiveSmallIntegerField(default=0)

现在我需要根据days_activation的值查询所有已过期的Process对象。

我试过了

from datetime import datetime, timedelta

Process.objects.filter(date_up__lte=datetime.now()-timedelta(days=F('days_activation')))

并收到以下错误消息:

TypeError:timedelta days 组件不支持的类型:F

我当然可以用 Python 来做:

filter (lambda x: x.date_up<=datetime.now() - timedelta(days=x.days_activation), 
        Process.objects.all ()), 

但我确实需要生成一个django.db.models.query.QuerySet

【问题讨论】:

    标签: django orm


    【解决方案1】:

    您必须扩展聚合。像下面这样:

    from django.db import models as DM
    
    class BaseSQL(object):
        function = 'DATE_SUB'
        template = '%(function)s(NOW(), interval %(expressions)s day)'
    
    class DurationAgr(BaseSQL, DM.Aggregate):
        def __init__(self, expression, **extra):
            super(DurationAgr, self).__init__(
                expression,
                output_field=DM.DateTimeField(),
                **extra
            )
    
    Process.objects.filter(date_up__lte=DurationAgr('days_activation'))
    

    希望它对你有用。 :)

    【讨论】:

      【解决方案2】:

      7 天 == 1 天 * 7

      F 是深黑色的 Django 魔法和遇到它的对象 必须属于相应的魔法圈才能处理。

      在您的情况下,django.db.models.query.filter 知道 F,但 datetime.timedelta 不知道。 因此,您需要将F 保留在timedelta 参数列表之外。 幸运的是,timedelta * int 的乘法得到了F 的支持, 所以以下可以工作:

      Process.objects.filter(date_up__lte=datetime.now()-timedelta(days=1)*F('days_activation'))
      

      事实证明,这个 will work with PostgreSQL,但不适用于 SQlite(Django 1.11 仅支持 +- 对于 timedelta, 可能是因为相应的 SQlite 限制)。

      【讨论】:

        【解决方案3】:

        您正在混合两个层:运行时层和数据库层。 F 函数只是一个帮助器,它允许您使用 django ORM 构建稍微复杂的查询。您正在使用 timedeltaFtogether 并期望 django ORM 足够聪明,可以将这些东西转换为原始 SQL,但正如我所见,它不能。也许我错了,对django ORM一无所知。

        无论如何,您可以使用额外的 extra 重写 ORM 调用,并使用等于 datetime.now()timedelta 的本机 SQL 函数手动构建 WHERE 子句。

        【讨论】:

          【解决方案4】:

          我尝试使用上述 Lutz Prechelt 的解决方案,但出现 MySQL 语法错误。 这是因为我们无法在 MySQL 中使用 INTERVAL 执行算术运算。

          所以,对于 MySQL,我的解决方案是创建一个自定义 DB 函数:

          class MysqlSubDate(Func):
              function = 'SUBDATE'
              output_field = DateField()
          

          使用示例:

          .annotate(remainded_days=MysqlSubDate('end_datetime', F('days_activation')))
          

          你也可以使用timedelta,它会被转换成INTERVAL

          .annotate(remainded_days=MysqlSubDate('end_datetime', datetime.timedelta(days=10)))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-03-30
            • 1970-01-01
            • 2015-10-02
            • 2015-05-02
            • 1970-01-01
            • 2020-04-03
            相关资源
            最近更新 更多