summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-12-03 11:56:22 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-12-03 11:58:55 +0100
commit2c20883cb0df8c56ecbb9a093fc4252410140b59 (patch)
treeacd8c2527619f1f9a1a6825346f1b78242c5d7ab
parent306fbf197a742b4b79e4ed23c94f5bbdbec9afdc (diff)
[4.0.x] Fixed #33333 -- Fixed setUpTestData() crash with models.BinaryField on PostgreSQL.
This makes models.BinaryField pickleable on PostgreSQL. Regression in 3cf80d3fcf7446afdde16a2be515c423f720e54d. Thanks Adam Zimmerman for the report. Backport of 2c7846d992ca512d36a73f518205015c88ed088c from main.
-rw-r--r--django/db/models/base.py13
-rw-r--r--docs/releases/3.2.10.txt4
-rw-r--r--tests/queryset_pickle/models.py1
-rw-r--r--tests/queryset_pickle/tests.py4
-rw-r--r--tests/test_utils/models.py1
-rw-r--r--tests/test_utils/test_testcase.py20
6 files changed, 42 insertions, 1 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 3c46afcf9f..218782c8df 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -553,6 +553,16 @@ class Model(metaclass=ModelBase):
state = self.__dict__.copy()
state['_state'] = copy.copy(state['_state'])
state['_state'].fields_cache = state['_state'].fields_cache.copy()
+ # memoryview cannot be pickled, so cast it to bytes and store
+ # separately.
+ _memoryview_attrs = []
+ for attr, value in state.items():
+ if isinstance(value, memoryview):
+ _memoryview_attrs.append((attr, bytes(value)))
+ if _memoryview_attrs:
+ state['_memoryview_attrs'] = _memoryview_attrs
+ for attr, value in _memoryview_attrs:
+ state.pop(attr)
return state
def __setstate__(self, state):
@@ -572,6 +582,9 @@ class Model(metaclass=ModelBase):
RuntimeWarning,
stacklevel=2,
)
+ if '_memoryview_attrs' in state:
+ for attr, value in state.pop('_memoryview_attrs'):
+ state[attr] = memoryview(value)
self.__dict__.update(state)
def _get_pk_val(self, meta=None):
diff --git a/docs/releases/3.2.10.txt b/docs/releases/3.2.10.txt
index 8df403e3d4..18f0f9f09a 100644
--- a/docs/releases/3.2.10.txt
+++ b/docs/releases/3.2.10.txt
@@ -10,4 +10,6 @@ Django 3.2.10 fixes a security issue with severity "low" and several bugs in
Bugfixes
========
-* ...
+* Fixed a regression in Django 3.2 that caused a crash of ``setUpTestData()``
+ with ``BinaryField`` on PostgreSQL, which is ``memoryview``-backed
+ (:ticket:`33333`).
diff --git a/tests/queryset_pickle/models.py b/tests/queryset_pickle/models.py
index 689a11f65e..9a033ffe95 100644
--- a/tests/queryset_pickle/models.py
+++ b/tests/queryset_pickle/models.py
@@ -46,6 +46,7 @@ class Happening(models.Model):
number1 = models.IntegerField(blank=True, default=standalone_number)
number2 = models.IntegerField(blank=True, default=Numbers.get_static_number)
event = models.OneToOneField(Event, models.CASCADE, null=True)
+ data = models.BinaryField(null=True)
class Container:
diff --git a/tests/queryset_pickle/tests.py b/tests/queryset_pickle/tests.py
index bf61960419..60d30cd8ce 100644
--- a/tests/queryset_pickle/tests.py
+++ b/tests/queryset_pickle/tests.py
@@ -16,6 +16,10 @@ class PickleabilityTestCase(TestCase):
def assert_pickles(self, qs):
self.assertEqual(list(pickle.loads(pickle.dumps(qs))), list(qs))
+ def test_binaryfield(self):
+ Happening.objects.create(data=b'binary data')
+ self.assert_pickles(Happening.objects.all())
+
def test_related_field(self):
g = Group.objects.create(name="Ponies Who Own Maybachs")
self.assert_pickles(Event.objects.filter(group=g.id))
diff --git a/tests/test_utils/models.py b/tests/test_utils/models.py
index b0497f12b9..57518aef90 100644
--- a/tests/test_utils/models.py
+++ b/tests/test_utils/models.py
@@ -8,6 +8,7 @@ class Car(models.Model):
class Person(models.Model):
name = models.CharField(max_length=100)
cars = models.ManyToManyField(Car, through='PossessedCar')
+ data = models.BinaryField(null=True)
class PossessedCar(models.Model):
diff --git a/tests/test_utils/test_testcase.py b/tests/test_utils/test_testcase.py
index 9b11168b13..0c2417c339 100644
--- a/tests/test_utils/test_testcase.py
+++ b/tests/test_utils/test_testcase.py
@@ -81,10 +81,15 @@ class TestDataTests(TestCase):
)
cls.non_deepcopy_able = NonDeepCopyAble()
+ cls.person_binary = Person.objects.create(name='Person', data=b'binary data')
+ cls.person_binary_get = Person.objects.get(pk=cls.person_binary.pk)
+
@assert_no_queries
def test_class_attribute_equality(self):
"""Class level test data is equal to instance level test data."""
self.assertEqual(self.jim_douglas, self.__class__.jim_douglas)
+ self.assertEqual(self.person_binary, self.__class__.person_binary)
+ self.assertEqual(self.person_binary_get, self.__class__.person_binary_get)
@assert_no_queries
def test_class_attribute_identity(self):
@@ -92,6 +97,21 @@ class TestDataTests(TestCase):
Class level test data is not identical to instance level test data.
"""
self.assertIsNot(self.jim_douglas, self.__class__.jim_douglas)
+ self.assertIsNot(self.person_binary, self.__class__.person_binary)
+ self.assertIsNot(self.person_binary_get, self.__class__.person_binary_get)
+
+ @assert_no_queries
+ def test_binaryfield_data_type(self):
+ self.assertEqual(bytes(self.person_binary.data), b'binary data')
+ self.assertEqual(bytes(self.person_binary_get.data), b'binary data')
+ self.assertEqual(
+ type(self.person_binary_get.data),
+ type(self.__class__.person_binary_get.data),
+ )
+ self.assertEqual(
+ type(self.person_binary.data),
+ type(self.__class__.person_binary.data),
+ )
@assert_no_queries
def test_identity_preservation(self):