我正在尝试创建一个自定义 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
,但我不确定我应该改变什么才能使这项工作正常进行?
您遇到的问题是由于 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)