Пользовательский общий поиск istruthy/isfalsy не работает на иностранных ключах

В моем Django-проекте мне нужен общий поиск istruthy/isfalsy, который бы работал с любым типом полей базы данных.

Пример models.py:

class MyModel(models.Model):
    my_charfield = models.CharField(...)
    my_decimalfield = models.DecimalField(...)
    my_datefield = models.DateField(...)
    my_boolfield = models.BooleanField(...)
    my_fkfield = models.ForeignKey(...)

Что я хочу уметь делать:

MyModel.objects.filter(my_charfield__isfalsy=True)
MyModel.objects.filter(my_decimalfield__isfalsy=True)
MyModel.objects.filter(my_datefield__isfalsy=True)
MyModel.objects.filter(my_boolfield__isfalsy=True)
MyModel.objects.filter(my_fkfield__isfalsy=True)

Я определил два пользовательских поиска для этого:

from django.db.models import Lookup, BooleanField, ForeignKey, Field
from django.db.models.fields import IntegerField, DecimalField, FloatField


class IsTruthy(Lookup):
    lookup_name = 'istruthy'

    def as_sql(self, compiler, connection):
        lhs, params = self.process_lhs(compiler, connection)

        if isinstance(self.lhs.output_field, BooleanField):
            return f"{lhs} = TRUE", params

        if isinstance(self.lhs.output_field, IntegerField):
            return f"{lhs} IS NOT NULL", params
        
        if isinstance(self.lhs.output_field, DecimalField):
            return f"{lhs} IS NOT NULL", params
        
        if isinstance(self.lhs.output_field, FloatField):
            return f"{lhs} IS NOT NULL", params

        if isinstance(self.lhs.output_field, ForeignKey):
            lhs = f"{lhs}_id"
            return f"{lhs} IS NOT NULL", params

        return f"{lhs} IS NOT NULL AND {lhs} <> ''", params


class IsFalsy(Lookup):
    lookup_name = 'isfalsy'

    def as_sql(self, compiler, connection):
        lhs, params = self.process_lhs(compiler, connection)

        if isinstance(self.lhs.output_field, BooleanField):
            return f"{lhs} = FALSE", params

        if isinstance(self.lhs.output_field, IntegerField):
            return f"{lhs} IS NULL", params

        if isinstance(self.lhs.output_field, DecimalField):
            return f"{lhs} IS NULL", params

        if isinstance(self.lhs.output_field, FloatField):
            return f"{lhs} IS NULL", params

        if isinstance(self.lhs.output_field, ForeignKey):
            lhs = f"{lhs}_id"
            return f"{lhs} IS NULL", params

        return f"{lhs} IS NULL OR {lhs} = ''", params


Field.register_lookup(IsTruthy)
Field.register_lookup(IsFalsy)

Эти пользовательские поиски работают со всеми полями, кроме ForeignKey. При использовании на ForeignKey возникает следующая ошибка:

django.core.exceptions.FieldError: Неподдерживаемый поиск 'isfalsy' для ForeignKey или join на поле не разрешены

Я пробовал различные варианты этого кода для обработки полей ForeignKey, но, похоже, ничего не работает. У меня также много проблем с отладкой, потому что pdb.set_trace(), похоже, ничего не делает в as_sql(); операторы печати не отображаются; операторы протоколирования, похоже, тоже не работают.

(Примечание: я знаю, что мои пользовательские поисковые запросы дают одинаковый результат независимо от того, использую ли я isfalsy=True или isfalsy=False. Такое поведение нормально в контексте моего проекта)

Я буду очень благодарен, если кто-нибудь поможет мне найти способ обработки FK или хотя бы поможет найти способ отладки!

Версия Django - 4.2

Спасибо!

РЕДАКТИРОВАТЬ :

Полный трассировщик :

Traceback (most recent call last):
  File "/home/luci/MyVENV/lib/python3.11/site-packages/IPython/core/interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-3-efef204fd6a5>", line 1, in <module>
    MyModel.objects.filter(my_fkfield__isfalsy=True)
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/query.py", line 1436, in filter
    return self._filter_or_exclude(False, args, kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/query.py", line 1454, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/query.py", line 1461, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1545, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1576, in _add_q
    child_clause, needed_inner = self.build_filter(
                                 ^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1491, in build_filter
    condition = self.build_lookup(lookups, col, value)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1312, in build_lookup
    lhs = self.try_transform(lhs, lookup_name)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/luci/MyVENV/lib/python3.11/site-packages/django/db/models/sql/query.py", line 1356, in try_transform
    raise FieldError(
django.core.exceptions.FieldError: Unsupported lookup 'isfalsy' for ForeignKey or join on the field not permitted.

У меня была похожая проблема, попробуйте сделать следующее, у меня сработало. Зарегистрируйте это явно для models.ForeignKey, примерно так:

class CustomLookup(models.Lookup):
    ...


models.Field.register_lookup(CustomLookup)
#  or more specific fields
models.ForeignKey.register_lookup(CustomLookup)

Вернуться на верх