我有以下型号:
class Process(models.Model):
title = models.Charfield(max_length=255)
date_up = models.DateTimeField(auto_now_add=True)
days_activation = models.PositiveSmallIntegerField(default=0)
现在,我需要根据Process
的值查询已过期的所有days_activation
个对象。
我试过
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
。
答案 0 :(得分:1)
您正在混合两个层:运行时层和数据库层。 F
函数只是一个帮助器,它允许您使用django ORM构建稍微复杂的查询。您正在使用timedelta
和F
,并期望django ORM足够聪明,可以将这些内容转换为原始SQL,但正如我所见,它不能。也许我错了,对django ORM一无所知。
无论如何,您可以使用额外的extra重写ORM调用,并使用等于datetime.now()
和timedelta
的本机SQL函数手动构建WHERE子句。
答案 1 :(得分:1)
您必须扩展Aggregate。如下所示:
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 :(得分:0)
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 :(得分:0)
我尝试使用上述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)))