Вы можете удалить аннотации из предложения SELECT, используя .values()
метод набора запросов. Проблема с .values()
состоит в том, что вы должны перечислять все имена, которые хотите сохранить, вместо имен, которые хотите пропустить, а .values()
возвращает словари вместо экземпляров модели.
Django внутренне отслеживает удаленных аннотаций в QuerySet.query.annotation_select_mask
. Таким образом, вы можете использовать его, чтобы сообщить Django, какие аннотации пропустить даже без .values()
:
class YourQuerySet(QuerySet):
def mask_annotations(self, *names):
if self.query.annotation_select_mask is None:
self.query.set_annotation_mask(set(self.query.annotations.keys()) - set(names))
else:
self.query.set_annotation_mask(self.query.annotation_select_mask - set(names))
return self
Затем вы можете написать:
invoices = (Invoice.objects
.annotate(has_notes=Exists(Note.objects.filter(invoice_id=OuterRef('pk'))))
.filter(has_notes=True)
.mask_annotations('has_notes')
)
, чтобы пропустить has_notes
из предложение SELECT и все еще получает отфильтрованные экземпляры счетов. Результирующий запрос SQL будет выглядеть примерно так:
SELECT invoice.id, invoice.foo FROM invoice
WHERE EXISTS(SELECT note.id, note.bar FROM notes WHERE note.invoice_id = invoice.id) = True
Просто обратите внимание, что annotation_select_mask
- это внутренний Django API, который может изменяться в будущих версиях без предупреждения.