使用 case 语句自定义 Django Transform,用于将字符串转换为 int 或 null

问题描述 投票:0回答:1

我正在尝试创建一个自定义 django 查找

as_int
,以便您可以轻松地比较和排序文本字段的内容(当文本字段仅包含数字时),并在格式错误时将它们视为 NULL。

例如:

Comment.objects.filter(body__as_int__gt=5)
# Should treat a comment with body="37" as 37
# Should treat a comment with body="abc" as NULL

这是我到目前为止所拥有的:

class AsIntTransform(Transform):
    lookup_name = "as_int"
    output_field = models.IntegerField()

    def as_sql(self, compiler, connection):
        case_expr = Case(
            When(
                Regex(self.lhs, r"^\d+$"),
                then=Cast(self.lhs, models.IntegerField()),
            ),
            default=Value(None),
        )
        return case_expr.as_sql(compiler, connection)


models.CharField.register_lookup(AsIntTransform)
models.TextField.register_lookup(AsIntTransform)

尝试使用上面的代码进行过滤会抛出:

    field_list = name.split(LOOKUP_SEP)
                 ^^^^^^^^^^
AttributeError: 'Col' object has no attribute 'split'

看起来它在这里指的“Col”是变换中的

self.lhs
,但我不确定我应该改变什么才能使这项工作正常进行?

python django django-queryset
1个回答
0
投票

您遇到的问题是由于 as_sql 方法中使用 self.lhs 属性的方式造成的。 Regex 函数需要列的字符串表示形式,但 self.lhs 是 Col 对象,这会导致错误。

要解决此问题,您需要确保该列被正确引用为 SQL 表达式。这是 AsIntTransform 类的更正版本:

from django.db import models
from django.db.models import Transform, Case, When, Value
from django.db.models.functions import Cast, Regex

class AsIntTransform(Transform):
    lookup_name = "as_int"
    output_field = models.IntegerField()

    def as_sql(self, compiler, connection):
        lhs_sql, params = self.lhs.as_sql(compiler, connection)
        case_expr = Case(
            When(
                Regex(lhs_sql, r"^\d+$"),
                then=Cast(lhs_sql, models.IntegerField()),
            ),
            default=Value(None),
        )
        return case_expr.as_sql(compiler, connection)

models.CharField.register_lookup(AsIntTransform)
models.TextField.register_lookup(AsIntTransform)

在此更正的版本中,self.lhs.as_sql(compiler, connection) 将 self.lhs 转换为 SQL 表达式,然后在 Regex 和 Cast 函数中使用。这应该可以解决 AttributeError 并允许您的自定义查找按预期运行。

您现在可以使用 as_int 查找进行过滤,如下所示:

Comment.objects.filter(body__as_int__gt=5)
© www.soinside.com 2019 - 2024. All rights reserved.