Django - аннотировать несколько полей из подзапроса - PullRequest
0 голосов
/ 29 марта 2020

Я работаю над проектом Django, в котором у меня есть набор запросов объектов 'A' (A.objects.all()), и мне нужно аннотировать несколько полей из подзапроса объектов 'B'. Проблема в том, что метод annotate может иметь дело только с одним типом поля на параметр (DecimalField, CharField и т. Д. c.), Поэтому для аннотирования нескольких полей я должен использовать что-то вроде:

A.objects.all().annotate(b_id          =Subquery(B_queryset.values('id')[:1],
                         b_name        =Subquery(B_queryset.values('name')[:1],
                         b_other_field =Subquery(B_queryset.values('other_field')[:1],
                         ... )

Что очень неэффективно, так как создает новый подзапрос / подвыбор в окончательном SQL для каждого поля, которое я хочу аннотировать. Я хотел бы использовать один и тот же Subselect с несколькими полями в его значениях values ​​() и аннотировать их все в наборе запросов А. Я хотел бы использовать что-то вроде этого:

b_subquery = Subquery(B_queryset.values('id', 'name', 'other_field', ...)[:1])
A.objects.all().annotate(b=b_subquery)

Но когда я пытаюсь сделать это (и получить доступ к первому элементу A.objects.all().annotate(b=b_subquery)[0]), возникает исключение:

{FieldError}Expression contains mixed types. You must set output_field.

И если я установлю Subquery(B_quer...[:1], output_field=ForeignKey(B, models.DO_NOTHING)), я получу исключение БД:

{ProgrammingError}subquery must return only one column

В двух словах, вся проблема в том, что у меня есть несколько B, которые " принадлежит "к A, поэтому мне нужно использовать подзапрос, чтобы для каждого A в A.objects.all(), выбрать конкретный c B и прикрепить его к этому A, используя OuterRefs и несколько фильтров (я хочу только несколько полей Б), который видит для меня тривиальную проблему.

Спасибо за любую помощь заранее!

1 Ответ

2 голосов
/ 29 марта 2020

В таких ситуациях я использую prefetch-related

a_qs = A.objects.all().prefetch_related(
    models.Prefetch('b_set',
        # NOTE: no need to filter with OuterRef (it wont work anyway)
        # Django automatically filter and matches B objects to A
        queryset=B_queryset,
        to_attr='b_records'
    )
)

Теперь a.b_records будет списком, содержащим a's связанных b объектов. В зависимости от того, как вы фильтруете B_queryset, этот список может быть ограничен только одним объектом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...