From a084c5d35a6d00abd261338a374a4424764b4aee Mon Sep 17 00:00:00 2001 From: AlexCLeduc Date: Mon, 19 Feb 2024 17:36:04 -0500 Subject: [PATCH] Fixed #35238 -- Fixed database serialization crash when base managers use prefetch_related(). Regression in 139135627650ed6aaaf4c755b82c3bd43f2b8f51 following deprecation in eedbf930287cb72e9afab1f7208c24b1146b0c4ec. --- django/db/backends/base/creation.py | 5 ++++- docs/releases/5.0.3.txt | 4 ++++ tests/backends/base/test_creation.py | 17 +++++++++++++++++ tests/backends/models.py | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py index cbac8a7f7672..6856fdb59676 100644 --- a/django/db/backends/base/creation.py +++ b/django/db/backends/base/creation.py @@ -135,7 +135,10 @@ def get_objects(): queryset = model._base_manager.using( self.connection.alias, ).order_by(model._meta.pk.name) - yield from queryset.iterator() + chunk_size = ( + 2000 if queryset._prefetch_related_lookups else None + ) + yield from queryset.iterator(chunk_size=chunk_size) # Serialize to a string out = StringIO() diff --git a/docs/releases/5.0.3.txt b/docs/releases/5.0.3.txt index e17fdd531f13..297e17d02359 100644 --- a/docs/releases/5.0.3.txt +++ b/docs/releases/5.0.3.txt @@ -24,3 +24,7 @@ Bugfixes * Fixed a regression in Django 5.0 that caused a crash of ``@sensitive_variables`` and ``@sensitive_post_parameters`` decorators on functions loaded from ``.pyc`` files (:ticket:`35187`). + +* Fixed a regression in Django 5.0 that caused a crash when reloading a test + database and a base queryset for a base manager used ``prefetch_related()`` + (:ticket:`35238`). diff --git a/tests/backends/base/test_creation.py b/tests/backends/base/test_creation.py index 9593e13462a0..7e760e88845a 100644 --- a/tests/backends/base/test_creation.py +++ b/tests/backends/base/test_creation.py @@ -14,6 +14,7 @@ Object, ObjectReference, ObjectSelfReference, + SchoolBus, SchoolClass, ) @@ -250,6 +251,22 @@ def test_serialize_db_to_string_base_manager(self): self.assertIn('"model": "backends.schoolclass"', data) self.assertIn('"year": 1000', data) + def test_serialize_db_to_string_base_manager_with_prefetch_related(self): + sclass = SchoolClass.objects.create( + year=2000, last_updated=datetime.datetime.now() + ) + bus = SchoolBus.objects.create(number=1) + bus.schoolclasses.add(sclass) + with mock.patch("django.db.migrations.loader.MigrationLoader") as loader: + # serialize_db_to_string() serializes only migrated apps, so mark + # the backends app as migrated. + loader_instance = loader.return_value + loader_instance.migrated_apps = {"backends"} + data = connection.creation.serialize_db_to_string() + self.assertIn('"model": "backends.schoolbus"', data) + self.assertIn('"model": "backends.schoolclass"', data) + self.assertIn(f'"schoolclasses": [{sclass.pk}]', data) + class SkipTestClass: def skip_function(self): diff --git a/tests/backends/models.py b/tests/backends/models.py index 99e9e86f44d2..1ed108c2b8c7 100644 --- a/tests/backends/models.py +++ b/tests/backends/models.py @@ -32,6 +32,20 @@ class SchoolClass(models.Model): objects = SchoolClassManager() +class SchoolBusManager(models.Manager): + def get_queryset(self): + return super().get_queryset().prefetch_related("schoolclasses") + + +class SchoolBus(models.Model): + number = models.IntegerField() + schoolclasses = models.ManyToManyField("SchoolClass") + objects = SchoolBusManager() + + class Meta: + base_manager_name = "objects" + + class VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ(models.Model): primary_key_is_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz = models.AutoField( primary_key=True