diff options
| author | Adam Johnson <me@adamj.eu> | 2025-09-05 11:04:27 +0100 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2025-10-16 14:52:22 -0400 |
| commit | a321d961b03d47b7f0c2e21a2370fc9e74c1889b (patch) | |
| tree | b57a131d63215b60f0c6fbfd8cb5f3e35fcff6e9 | |
| parent | e097e8a12f21a4e92594830f1ad1942b31916d0f (diff) | |
Refs #28586 -- Made fetch modes pickle as singletons.
This change ensures that we don’t create new instances of fetch modes
when pickling and unpickling, saving memory and preserving their singleton
nature.
| -rw-r--r-- | django/db/models/fetch_modes.py | 9 | ||||
| -rw-r--r-- | tests/queryset_pickle/tests.py | 23 |
2 files changed, 32 insertions, 0 deletions
diff --git a/django/db/models/fetch_modes.py b/django/db/models/fetch_modes.py index a22ccd8a23..2b5e6aa212 100644 --- a/django/db/models/fetch_modes.py +++ b/django/db/models/fetch_modes.py @@ -16,6 +16,9 @@ class FetchOne(FetchMode): def fetch(self, fetcher, instance): fetcher.fetch_one(instance) + def __reduce__(self): + return "FETCH_ONE" + FETCH_ONE = FetchOne() @@ -36,6 +39,9 @@ class FetchPeers(FetchMode): else: fetcher.fetch_one(instance) + def __reduce__(self): + return "FETCH_PEERS" + FETCH_PEERS = FetchPeers() @@ -48,5 +54,8 @@ class Raise(FetchMode): field_name = fetcher.field.name raise FieldFetchBlocked(f"Fetching of {klass}.{field_name} blocked.") from None + def __reduce__(self): + return "RAISE" + RAISE = Raise() diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py index acdb582a0a..074a8ed550 100644 --- a/tests/queryset_pickle/tests.py +++ b/tests/queryset_pickle/tests.py @@ -351,6 +351,29 @@ class PickleabilityTestCase(TestCase): event.edition_set.create() self.assert_pickles(event.edition_set.order_by("event")) + def test_fetch_mode_fetch_one(self): + restored = pickle.loads(pickle.dumps(self.happening)) + self.assertIs(restored._state.fetch_mode, models.FETCH_ONE) + + def test_fetch_mode_fetch_peers(self): + Happening.objects.create() + objs = list(Happening.objects.fetch_mode(models.FETCH_PEERS)) + self.assertEqual(objs[0]._state.fetch_mode, models.FETCH_PEERS) + self.assertEqual(len(objs[0]._state.peers), 2) + + restored = pickle.loads(pickle.dumps(objs)) + + self.assertIs(restored[0]._state.fetch_mode, models.FETCH_PEERS) + # Peers not restored because weak references are not picklable. + self.assertEqual(restored[0]._state.peers, ()) + + def test_fetch_mode_raise(self): + objs = list(Happening.objects.fetch_mode(models.RAISE)) + self.assertEqual(objs[0]._state.fetch_mode, models.RAISE) + + restored = pickle.loads(pickle.dumps(objs)) + self.assertIs(restored[0]._state.fetch_mode, models.RAISE) + class InLookupTests(TestCase): @classmethod |
