来自 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(我的生产和本地开发数据库)的功能联系人不同:
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