【问题标题】:from django.db.models.Value not including quotes when converting to query string来自 django.db.models.Value 转换为查询字符串时不包括引号
【发布时间】: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 查询正常工作的最佳解决方法是什么?

【问题讨论】:

    标签: sqlite django-models concatenation coalesce


    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-03
      • 1970-01-01
      相关资源
      最近更新 更多