From 3ff32c50d143d8a498f9a5dfef1a31b16a7456fe Mon Sep 17 00:00:00 2001 From: Ken Nzioka Date: Wed, 22 Oct 2025 10:48:23 +0300 Subject: [PATCH] Fixed #36674 -- Fixed memory leak in select_related(). --- django/db/models/sql/compiler.py | 17 ++++++++--------- tests/select_related/tests.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 0e483dc4f6..14603d5773 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -228,6 +228,13 @@ class SQLCompiler: ] return expressions + @classmethod + def get_select_from_parent(cls, klass_info): + for ki in klass_info["related_klass_infos"]: + if ki["from_parent"]: + ki["select_fields"] = klass_info["select_fields"] + ki["select_fields"] + cls.get_select_from_parent(ki) + def get_select(self, with_col_aliases=False): """ Return three values: @@ -300,15 +307,7 @@ class SQLCompiler: related_klass_infos = self.get_related_selections(select, select_mask) klass_info["related_klass_infos"] = related_klass_infos - def get_select_from_parent(klass_info): - for ki in klass_info["related_klass_infos"]: - if ki["from_parent"]: - ki["select_fields"] = ( - klass_info["select_fields"] + ki["select_fields"] - ) - get_select_from_parent(ki) - - get_select_from_parent(klass_info) + self.get_select_from_parent(klass_info) ret = [] col_idx = 1 diff --git a/tests/select_related/tests.py b/tests/select_related/tests.py index 41ed350cf3..59d1270aa0 100644 --- a/tests/select_related/tests.py +++ b/tests/select_related/tests.py @@ -1,6 +1,9 @@ +import gc + from django.core.exceptions import FieldError from django.db.models import FETCH_PEERS from django.test import SimpleTestCase, TestCase +from django.test.utils import garbage_collect from .models import ( Bookmark, @@ -57,6 +60,17 @@ class SelectRelatedTests(TestCase): "Amanita muscaria" ) + def setup_gc_debug(self): + self.addCleanup(gc.set_debug, 0) + self.addCleanup(gc.enable) + gc.disable() + garbage_collect() + gc.set_debug(gc.DEBUG_SAVEALL) + + def assert_no_memory_leaks(self): + garbage_collect() + self.assertEqual(gc.garbage, []) + def test_access_fks_without_select_related(self): """ Normally, accessing FKs doesn't fill in related objects @@ -128,6 +142,11 @@ class SelectRelatedTests(TestCase): ) self.assertEqual(s.id + 10, s.a) + def test_select_related_memory_leak(self): + self.setup_gc_debug() + list(Species.objects.select_related("genus")) + self.assert_no_memory_leaks() + def test_certain_fields(self): """ The optional fields passed to select_related() control which related