From 74b0837bef6270a78f55878c488561f81a6339f7 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Thu, 11 May 2017 21:04:52 -0400 Subject: [PATCH] [1.11.x] Fixed #28188 -- Fixed crash when pickling model fields. Regression in d2a26c1a90e837777dabdf3d67ceec4d2a70fb86. Thanks Adam Alton for the report and test, and Adam Johnson for suggesting the fix. Backport of a9874d48b1b9d91988b9f299726ec4f559fb2f75 from master --- django/db/models/fields/__init__.py | 6 +++++- docs/releases/1.11.2.txt | 2 ++ tests/model_fields/tests.py | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 0af100efd9d..8d40c77345a 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -514,7 +514,11 @@ class Field(RegisterLookupMixin): # instance. The code below will create a new empty instance of # class self.__class__, then update its dict with self.__dict__ # values - so, this is very close to normal pickle. - return _empty, (self.__class__,), self.__dict__ + state = self.__dict__.copy() + # The _get_default cached_property can't be pickled due to lambda + # usage. + state.pop('_get_default', None) + return _empty, (self.__class__,), state return _load_field, (self.model._meta.app_label, self.model._meta.object_name, self.name) diff --git a/docs/releases/1.11.2.txt b/docs/releases/1.11.2.txt index fd6b7083e9e..558b3e40c37 100644 --- a/docs/releases/1.11.2.txt +++ b/docs/releases/1.11.2.txt @@ -18,3 +18,5 @@ Bugfixes * Fixed ``django.utils.http.is_safe_url()`` crash on invalid IPv6 URLs (:ticket:`28142`). + +* Fixed regression causing pickling of model fields to crash (:ticket:`28188`). diff --git a/tests/model_fields/tests.py b/tests/model_fields/tests.py index a9c9794a3cd..00f83381281 100644 --- a/tests/model_fields/tests.py +++ b/tests/model_fields/tests.py @@ -1,3 +1,5 @@ +import pickle + from django import forms from django.db import models from django.test import SimpleTestCase, TestCase @@ -76,6 +78,13 @@ class BasicFieldTests(TestCase): self.assertIsNotNone(f1) self.assertNotIn(f2, (None, 1, '')) + def test_field_instance_is_picklable(self): + """Field instances can be pickled.""" + field = models.Field(max_length=100, default='a string') + # Must be picklable with this cached property populated (#28188). + field._get_default + pickle.dumps(field) + class ChoicesTests(SimpleTestCase):