From 73c987eb3b6a96300f7238dff32caf53cafe2098 Mon Sep 17 00:00:00 2001 From: Samriddha9619 Date: Mon, 8 Dec 2025 22:11:31 +0530 Subject: Fixed #36701 -- Fixed memory leak in ModelState. --- django/db/models/base.py | 3 +++ tests/model_regress/models.py | 4 ++++ tests/model_regress/test_state.py | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index 8d7cbdd32d..ad3f0c5e23 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -494,6 +494,9 @@ class ModelState: state.pop("peers", None) return state + def __del__(self): + self.fields_cache.clear() + class Model(AltersData, metaclass=ModelBase): def __init__(self, *args, **kwargs): diff --git a/tests/model_regress/models.py b/tests/model_regress/models.py index 350850393a..2af882f20f 100644 --- a/tests/model_regress/models.py +++ b/tests/model_regress/models.py @@ -45,6 +45,10 @@ class Worker(models.Model): return self.name +class WorkerProfile(models.Model): + worker = models.OneToOneField(Worker, on_delete=models.CASCADE) + + class NonAutoPK(models.Model): name = models.CharField(max_length=10, primary_key=True) diff --git a/tests/model_regress/test_state.py b/tests/model_regress/test_state.py index 8b0f087829..8abcc58382 100644 --- a/tests/model_regress/test_state.py +++ b/tests/model_regress/test_state.py @@ -1,7 +1,33 @@ +import gc + from django.db.models.base import ModelState, ModelStateFieldsCacheDescriptor from django.test import SimpleTestCase +from django.test.utils import garbage_collect + +from .models import Worker, WorkerProfile class ModelStateTests(SimpleTestCase): def test_fields_cache_descriptor(self): self.assertIsInstance(ModelState.fields_cache, ModelStateFieldsCacheDescriptor) + + def test_one_to_one_field_cycle_collection(self): + self.addCleanup(gc.set_debug, gc.get_debug()) + gc.set_debug(gc.DEBUG_SAVEALL) + + def clear_garbage(): + del gc.garbage[:] + + self.addCleanup(clear_garbage) + + worker = Worker() + profile = WorkerProfile(worker=worker) + worker_id = id(worker) + + del worker + del profile + + garbage_collect() + + leaked = [obj for obj in gc.garbage if id(obj) == worker_id] + self.assertEqual(leaked, []) -- cgit v1.3