Django 3.2多DateTimeField模型如何实现带日期运算的ORM查询
Django 3.2 实现带字段日期运算的查询方案
首先需要校正你现有代码的逻辑问题:你写的criterion1 = Q(expiry_date__gt=date.today())是查询还未过期的条目,和你需要的「已过期」逻辑相反,需要调整为小于判断。
Django 3.2 原生支持F()表达式与timedelta直接做算术运算,不需要额外依赖,以下是两种实现方式:
方案1:直接在filter中写运算逻辑(推荐,性能更高)
不需要额外字段注解,直接在查询条件中完成日期计算:
from django.db.models import F, Q from datetime import date, timedelta # 条件1:已过期,过期时间早于今日 criterion1 = Q(expiry_date__lt=date.today()) # 条件2:仍在安全食用期:过期时间 + 可安全食用天数 >= 今日 criterion2 = Q(expiry_date + timedelta(days=1)*F("safe_to_eat_days_after_expiry") >= date.today()) # 合并条件查询 queryset = FoodItem.objects.filter(criterion1 & criterion2)
你也可以把条件2改写为等价的反向运算,可读性和执行效率没有差异:
criterion2 = Q(expiry_date__gte=date.today() - F("safe_to_eat_days_after_expiry"))
方案2:先注解计算最后安全食用日期再过滤
如果后续逻辑也需要用到「最后可食用日期」这个字段,可以先用annotate把计算结果存为临时字段再过滤:
from django.db.models import F, ExpressionWrapper, DateTimeField from datetime import date, timedelta queryset = FoodItem.objects.annotate( # 计算最后安全食用日期 last_safe_date = ExpressionWrapper( F("expiry_date") + timedelta(days=1)*F("safe_to_eat_days_after_expiry"), output_field=DateTimeField() ) ).filter( expiry_date__lt=date.today(), last_safe_date__gte=date.today() )
注意事项
由于你的expiry_date是DateTimeField类型,date.today()是日期类型,Django会默认将date.today()转为当日0点的datetime对象做比较。如果你的业务需要精确到时分秒判断过期,可以把date.today()替换为datetime.now()调整逻辑。
内容的提问来源于stack exchange,提问作者Homunculus Reticulli
相关产品推荐
相关产品推荐

