Django включает пользовательские @свойства модели ORM в набор queryset для дальнейшего использования в фильтрах клиентов

Привет и заранее спасибо.

Я определил пользовательское свойство в моей модели ORM:

class MyModel(BaseModel):
  optional_prop_1 = models.ForeignKey(Model, null=True)
  optional_prop_2 = models.ForeignKey(AnotherModel, null=True)
  optional_prop_2 = models.ForeignKey(DifferentModel, null=True)

  @property
  def external_reference(self):
    if self.optional_prop_1:
       return self.optional_prop_1
    if self.optional_prop_2:
       return self.optional_prop_2
    ...

Все эти три поля имеют общее поле, к которому я хочу получить доступ внутри моего пользовательского запроса filer, но поскольку external_reference определено как "виртуальное" свойство, я знаю, что не могу получить к нему доступ внутри queryset, поэтому, когда я делаю это, это действительно работает:

queryset.filter(top_level_relation__my_model__external_reference__common_field="some_value")

Кажется, я понял, что мне нужно как-то преобразовать мое "виртуальное" свойство в поле динамически с помощью пользовательских models.Manager и queryset.annotate(), но, похоже, это не работает. Я попробовал следующее:

def _get_external_reference(model) -> str:
    if model.optional_prop_1:
        return "optional_prop_1"
    elif model.optional_prop_2:
        return "optional_prop_1"
    ...

    return ""

def get_queryset(self):
    external_reference = _get_external_reference(self.model)

    return super().get_queryset().annotate(external_reference=models.F(external_reference))

Но внутри моего пользовательского фильтра я всегда получаю Related Field got invalid lookup: external_reference говоря мне, что это поле не существует в наборе запросов. Есть идеи, как преобразовать свойство (@property) в поле, которое я мог бы использовать позже внутри queryset

Ошибка в вашем коде заключается в том, что вы пытаетесь получить доступ к свойствам модели непосредственно в функции _get_external_reference, но аргументом, передаваемым этой функции, является не экземпляр MyModel, а сам класс модели.

Кроме того, возвращаемое значение _get_external_reference является строкой, представляющей имя свойства, но вы используете эту строку непосредственно в вызове annotate в качестве имени поля.

Для решения этой проблемы можно использовать выражения Func и F для динамического создания выражения для аннотированного поля на основе значений полей optional_prop_1, optional_prop_2 и optional_prop_3:

from django.db.models import F, Func, Value

class MyModelManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().annotate(
            external_reference=Func(
                F('optional_prop_1'),
                F('optional_prop_2'),
                F('optional_prop_3'),
                function='COALESCE',
                output_field=models.ForeignKey(Model),
            )
        )

class MyModel(BaseModel):
    ...
    objects = MyModelManager()

    class Meta:
        ...
Вернуться на верх