select_related removed when performing a deletion

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?

:waving_hand: hello there!

Looking at the blame trace for this change it seems to have been this way forever so the reasoning behind it might be lost.

I guess you could try removing it and running the test suite to determine if it breaks anything.

My hot take is that it that this line precedes the additions of deletion signals and on_delete as we know them today and that point the ORM was always issuing chains DELETE queries where select_related doesn’t make much sense.

If that’s the case the test suite should agree and removing it with tested support should be acceptable.

2 Likes

thanks for the reply! I’ll experiment with changing it in my application and if it seems fine i’ll consider submitting a patch to django itself. Would it be appropriate to file this as a bug report?

I think so yes, assuming you can demonstrate the test suite passes when removed.