Аннотация с подзапросом с множественным результатом в Django
Я использую базу данных postgresql в своем проекте, и я использую приведенный ниже пример из документации django.
from django.db.models import OuterRef, Subquery
newest = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at')
Post.objects.annotate(newest_commenter_email=Subquery(newest.values('email')[:1]))
но вместо последнего письма комментатора мне нужны последние два письма комментатора. Я изменился[:1]
к [:2]
но возникло это исключение: ProgrammingError: more than one row returned by a subquery used as an expression
.
1 ответ
Вам нужно каким-то образом агрегировать результаты подзапроса: возможно, используя ARRAY()
построить.
Вы можете создать подкласс Subquery
сделать это:
class Array(Subquery):
template = 'ARRAY(%(subquery)s)`
output_field = ArrayField(base_field=models.TextField())
(Вы можете использовать более автоматический метод получения поля вывода, но пока это должно работать для вас: см. https://schinckel.net/2019/07/30/subquery-and-subclasses/ для получения дополнительных сведений).
Тогда вы можете использовать:
posts = Post.objects.annotate(
newest_commenters=Array(newest.values('email')[:2]),
)
Причина этого в том, что коррелированный подзапрос в postgres может возвращать только одну строку с одним столбцом. Вы можете использовать этот механизм для работы с несколькими строками и, возможно, использовать конструкцию JSONB, если вам нужно несколько столбцов.