From 94ea79be137f3cb30949bf82198e96e094f2650d Mon Sep 17 00:00:00 2001 From: Gert Burger Date: Fri, 7 Aug 2020 10:02:29 +0200 Subject: [PATCH] Fixed #31863 -- Prevented mutating model state by copies of model instances. Regression in bfb746f983aa741afa3709794e70f1e0ab6040b5. --- django/db/models/base.py | 5 ++++- tests/model_regress/tests.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index 3792ffb90e..7c7bd2d7ee 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -546,7 +546,10 @@ class Model(metaclass=ModelBase): def __getstate__(self): """Hook to allow choosing the attributes to pickle.""" - return self.__dict__ + state = self.__dict__.copy() + state['_state'] = copy.copy(state['_state']) + state['_state'].fields_cache = state['_state'].fields_cache.copy() + return state def __setstate__(self, state): pickled_version = state.get(DJANGO_VERSION_PICKLE_KEY) diff --git a/tests/model_regress/tests.py b/tests/model_regress/tests.py index ba496d1f26..1a4e689b45 100644 --- a/tests/model_regress/tests.py +++ b/tests/model_regress/tests.py @@ -1,3 +1,4 @@ +import copy import datetime from operator import attrgetter @@ -256,3 +257,17 @@ class EvaluateMethodTest(TestCase): dept = Department.objects.create(pk=1, name='abc') dept.evaluate = 'abc' Worker.objects.filter(department=dept) + + +class ModelFieldsCacheTest(TestCase): + def test_fields_cache_reset_on_copy(self): + department1 = Department.objects.create(id=1, name='department1') + department2 = Department.objects.create(id=2, name='department2') + worker1 = Worker.objects.create(name='worker', department=department1) + worker2 = copy.copy(worker1) + + self.assertEqual(worker2.department, department1) + # Changing related fields doesn't mutate the base object. + worker2.department = department2 + self.assertEqual(worker2.department, department2) + self.assertEqual(worker1.department, department1)