来自 django.db.models.Value 转换为查询字符串时不包括引号

Posted

技术标签:

【中文标题】来自 django.db.models.Value 转换为查询字符串时不包括引号【英文标题】:from django.db.models.Value not including quotes when converting to query string 【发布时间】:2022-01-13 09:11:22 【问题描述】:

我在 Django 3.2.8 的 QuerySet 函数中使用 django.db.models.Value 表达式。传递字符串值时(在下面的示例中为"|"),转换为查询字符串时无法添加',从而导致查询失败。

from django.db import models
from django.db.models import F, Value
from django.db.models.functions import Concat

class PurchaseOrder(models.Model):
    id = models.AutoField(primary_key=True)
    customer_name = models.CharField(max_length=50)
    date = models.DateField()
    

qs = PurchaseOrder.objects.annotate(
    concat=Concat(
        F("customer_name"),
        Value("~"),
        F("date"),
        output_field=models.CharField(),
    )
).values("concat")

>>> print(str(qs.query))
SELECT CONCAT("purchaseorder"."customer_name", CONCAT(~, "purchaseorder"."date")) AS "concat" FROM "purchaseorder"

从上面的结果可以看出,~ 字符缺少两个' 它应该被环绕:CONCAT('~', "purchaseorder"."date")

这是表达式Value 的预期功能还是应该报告的错误? 我倾向于认为这是一个错误,原因如下:

我最初通过将其写为Value("'~'") 解决了上述问题,其中两个' 在我的字符串中。但是,在单元测试期间在 sqlite3 数据库中运行查询时,查询中出现错误。我意识到 sqlite 的语法与 Postgres(我的生产和本地开发数据库)的功能联系人不同:

Postgres:CONCAT([args]) Sqlite:arg1 || arg2 ...

在 sqlite 的情况下,Django 还使用Coalesce 包装连接中的每个参数:

COALESCE(arg1, '') || COALESCE(arg2, '')

在 sqlite 中由此产生的查询如下所示:

... COALESCE("purchaseorder"."customer_name", ) || COALESCE('~', ) || COALESCE("purchaseorder"."date",)

(注意这次我在Value("'~'")里面通过了'

上述查询将给出错误,因为COALESCE 中的第二个参数不能为空('' 是可接受的输入)。

如果上述问题是由错误引起的,那么使 sqlite 查询正常工作的最佳解决方法是什么?

【问题讨论】:

【参考方案1】:

这似乎是一个错误:

django.db.models.functions.text.ConcatPair内部,方法as_sqlite调用方法coalesce

class ConcatPair(Func):
    ...

    def as_sqlite(self, compiler, connection, **extra_context):
        coalesced = self.coalesce()
        return super(ConcatPair, coalesced).as_sql(
            compiler, connection, template='%(expressions)s', arg_joiner=' || ',
            **extra_context
        )

    ...

    def coalesce(self):
        # null on either side results in null for expression, wrap with coalesce
        c = self.copy()
        c.set_source_expressions([
            Coalesce(expression, Value('')) for expression in c.get_source_expressions()
        ])
        return c

这是使用 COALESCE 函数将参数包装在串联中的机制,可以看出,它使用的是Value('')。这意味着表达式 Value 应直接与字符串一起使用,因为上面的表达式预计会在 SQL 中生成 COALESCE([expression], '')

我还没有弄清楚Value 中的确切问题是什么,但下面是串联中COALESCE 问题的解决方法。只需覆盖ConcatPair 中的coalesce 方法,在Value 中添加'

from django.db.models.functions import ConcatPair, Coalesce


def _coalesce(self):
    c = self.copy()
    c.set_source_expressions(
        [Coalesce(expression, Value("''")) for expression in c.get_source_expressions()]
    )
    return c


ConcatPair.coalesce = _coalesce

【讨论】:

以上是关于来自 django.db.models.Value 转换为查询字符串时不包括引号的主要内容,如果未能解决你的问题,请参考以下文章

书评:男人来自火星,女人来自金星-约翰.格雷

为啥 WCF 服务能够处理来自不同进程的调用而不是来自线程的调用

来自 viewDidAppear 的 Segue 调用有效,但不是来自 viewWillAppear

求职作业帮 C++方向面经

来自 CWnd 的 ReleaseDC 覆盖来自 winuser 的 ReleaseDC

来自麦克风的声音与来自扬声器的声音