Пользовательское преобразование Django с оператором case для приведения строки к int или null
Я пытаюсь создать пользовательский django lookup 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
в трансформации, но я не уверен, что я должен изменить, чтобы это сработало?
Проблема, с которой вы столкнулись, связана с тем, как атрибут self.lhs используется в методе as_sql. Функция 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)