Today whilst investigating an n+1 query issue, we noticed that the following code is present in the django/db/models/query.py:
class QuerySet(AltersData):
...
def delete(self):
"""Delete the records in the current QuerySet."""
...
del_query.query.select_related = False
The line del_query.query.select_related = False appears to be removing all select_related clauses from the queryset. This line happens before we fire pre_delete or post_delete signals. Since these signals are applied when running queryset deletions, this means that if we have some code such as
my_query = MyModel.objects.all().select_related('my_foreign_key')
my_query.delete()
any signal which attempts to take advantage of the select_related clause will now result in an n+1 query
@receiver(post_delete, sender=MyModel)
def my_signal(sender, instance, **kwargs):
# not prefetched, so does a db look up for every instance in the my_query
if instance.my_foreign_key:
....
Does anyone know why we remove the select_related clause before deletion and if there’s a way to avoid n+1 issues like this in pre_delete/post_delete signals?