Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #35238 -- Fixed database serialization crash when base managers use prefetch_related(). #17889

Merged
merged 1 commit into from Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion django/db/backends/base/creation.py
Expand Up @@ -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()
Expand Down
4 changes: 4 additions & 0 deletions docs/releases/5.0.3.txt
Expand Up @@ -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`).
17 changes: 17 additions & 0 deletions tests/backends/base/test_creation.py
Expand Up @@ -14,6 +14,7 @@
Object,
ObjectReference,
ObjectSelfReference,
SchoolBus,
SchoolClass,
)

Expand Down Expand Up @@ -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):
Expand Down
14 changes: 14 additions & 0 deletions tests/backends/models.py
Expand Up @@ -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
Expand Down