I have an event object, there are other objects besides Notes
that have a generic relation to Event
, and which don't have an active field. Now I want to be able to write a query which excludes all Events
where the active field of the Notes
is False. So I tried doing the following.
queryset = Event.objects.all()
filters = (
Q(content_type__model='notes') &
Q(note__active=False)
)
queryset = queryset.exclude(filters)
This didn't work because it's running the query separately, and when it tries to execute for items for which there are no content_object or which are not of type Notes
, it fails and gives the following error:
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'
)
The usual way to do that using the Django ORM is with a subquery:
Event.objects.exclude(
content_type=note_content_type,
object_id__in=Notes.objects.filter(active=False),
)
The sad thing is that this can be expressed in SQL with a join instead of a subquery, but the ORM doesn't make that easy.
In some cases PostgreSQL's query planner can optimize the subquery into a hash semi join, so don't dismiss the subquery in preference to raw SQL without benchmarking on the data.