You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django使用ExpressionWrapper在SQLite上报错Invalid connector for timedelta

错误原因

Django的SQLite数据库后端仅支持对timedelta类型执行+/-运算,你代码中timezone.timedelta(days=1) * models.F('duration')的乘法操作触发了SQLite后端的限制,因此单元测试报错;而PostgreSQL后端原生支持该乘法操作,所以生产环境运行正常。

解决方案

方案1(推荐,适配Django 3.2及以上版本)

使用Django内置的跨数据库兼容函数MakeInterval生成时间间隔,自动适配PostgreSQL和SQLite的语法,无需手动判断数据库类型,修改后的filter_expiration方法代码如下:

from django.db.models.functions import MakeInterval

class AccessDurationQuerySet(models.QuerySet):

    def filter_expiration(self, is_expired: bool = False):
        """
        To filter whether AccessDuration already expired or yet.
        don't use the same `expiration_date` as expression key,
        because it will clash with `AccessDuration.expiration_date` property.
        """
        kwargs = {'_expiration_date__gte': Now()}
        if is_expired:
            del kwargs['_expiration_date__gte']
            kwargs['_expiration_date__lt'] = Now()

        return self.all().annotate(
            _expiration_date=models.ExpressionWrapper(
                models.F('lab_start_date') + MakeInterval(days=models.F('duration')),
                output_field=models.DateTimeField()
            )
        ).filter(**kwargs)

方案2(兼容Django 3.2以下版本)

如果使用的Django版本低于3.2没有内置MakeInterval函数,可以通过判断当前数据库类型动态生成兼容的表达式,代码如下:

from django.db import connection

class AccessDurationQuerySet(models.QuerySet):

    def filter_expiration(self, is_expired: bool = False):
        """
        To filter whether AccessDuration already expired or yet.
        don't use the same `expiration_date` as expression key,
        because it will clash with `AccessDuration.expiration_date` property.
        """
        kwargs = {'_expiration_date__gte': Now()}
        if is_expired:
            del kwargs['_expiration_date__gte']
            kwargs['_expiration_date__lt'] = Now()

        if connection.vendor == 'sqlite':
            # SQLite适配:将天数转换为微秒直接相加
            expiration_expr = models.ExpressionWrapper(
                models.F('lab_start_date') + models.F('duration') * 86400 * 1000000,
                output_field=models.DateTimeField()
            )
        else:
            # PostgreSQL原有逻辑保持不变
            expiration_expr = models.ExpressionWrapper(
                models.F('lab_start_date') + (timezone.timedelta(days=1) * models.F('duration')),
                output_field=models.DateTimeField()
            )

        return self.all().annotate(
            _expiration_date=expiration_expr
        ).filter(**kwargs)

内容的提问来源于stack exchange,提问作者binpy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 02:45:05