Добавьте аннотации ко всем запросам Django ORM, которые проходят через модель

#django #django-models #django-orm

Вопрос:

Абстрактпользователь Django имеет поля для first_name и last_name , и get_full_name метод.

Если бы я хотел отфильтровать пользователей на основе поиска их полного имени, я могу добавить пользовательский менеджер моделей для Пользователя, у которого есть метод примерно такой:

 def filter(self, *args, **kwargs):
    queryset = super().get_queryset()
    if any(key for key in kwargs if key.startswith('full_name')):
        queryset = queryset.annotate(
            full_name=Concat(models.F('first_name'), models.Value(' '), models.F('last_name'))
        )
    return queryset.filter(*args, **kwargs)
 

Это значит, что я могу делать такие вещи, как:

 User.objects.filter(full_name='Foo Bar')
User.objects.filter(full_name__icontains='foo b')
... etc
 

Но, конечно, это будет работать только при прямой фильтрации пользователей выше. Если у вас есть объекты Post, которые принадлежат пользователю, вы получите это:

 Post.objects.filter(user__full_name='Foo Bar')
...
FieldError: Related Field got invalid lookup: full_name
 

Итак, вопрос в том, есть ли способ (без изменения всех моделей для создания пользовательских менеджеров), чтобы Django всегда распознавал данный термин (в данном случае «полное имя») во всех соединениях с таблицей данной модели и аннотировал необходимое значение, чтобы его можно было отфильтровать?

Примечание: Я знаю, что, конечно, мог бы добавить full_name в качестве поля пользователя и попытаться убедиться, что оно всегда актуально, но я специально стараюсь не добавлять эти денормализованные данные в базу данных.

Комментарии:

1. Похоже, что это невозможно отсюда

2. В частности, это заявление: Base managers aren’t used when querying on related models, or when accessing a one-to-many or many-to-many relationship. For example, if the Question model from the tutorial had a deleted field and a base manager that filters out instances with deleted=True, a queryset like Choice.objects.filter(question__name__startswith='What') would include choices related to deleted questions.

3. Вы, вероятно, могли бы просто сделать что-то вроде Post.objects.filter(user__in=filtered_and_annotated_user_queryset)