Django Условное поле GenericRelated Запрос, вызывающий объект 'GenericRelation', не имеет атрибута 'field'

У меня есть объект события, есть и другие объекты, кроме Notes которые имеют общее отношение к Eventи которые не имеют активного поля. Теперь я хочу написать запрос, который исключает все Events где активное поле Notes Неверно Поэтому я попытался сделать следующее.

queryset = Event.objects.all()
filters = (
        Q(content_type__model='notes') &
        Q(note__active=False)
    )
queryset = queryset.exclude(filters)

Это не сработало, потому что он выполняет запрос отдельно, и когда он пытается выполнить для элементов, для которых нет content_object или которые не имеют тип Notes, это терпит неудачу и дает следующую ошибку:

AttributeError 'GenericRelation' object has no attribute 'field'.

class Event(models.Model):
    content_type = models.ForeignKey(ContentType, null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True, blank=True)
    content_object = GenericForeignKey('content_type', 'object_id')

class Notes(models.Model):
    active = models.BooleanField(default=True)
    event_log = GenericRelation(
        'Event',
        related_query_name='note'
    )

2 ответа

Решение

Обычный способ сделать это с помощью Django ORM - с помощью подзапроса:

Event.objects.exclude(
    content_type=note_content_type,
    object_id__in=Notes.objects.filter(active=False),
)

Печально то, что это можно выразить в SQL с помощью объединения вместо подзапроса, но ORM не делает это простым.

В некоторых случаях планировщик запросов PostgreSQL может оптимизировать подзапрос в хеш-полусоединение, поэтому не отбрасывайте подзапрос в предпочтении по сравнению с необработанным SQL без проверки производительности данных.

Другой подход заключается в использовании условного выражения, например

from django.db import models
queryset = Event.objects.all()
queryset = queryset.annotate(
    is_inactive_note=models.Case(
        models.When(
            content_type__model='notes',
            note__active=False,
            then=models.Value(True),
        ),
        default=models.Value(False),
        output_field=models.BooleanField(),
    )
).filter(is_inactive_note=False)
Другие вопросы по тегам