[3.2.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.
This commit is contained in:
parent
0cf2d48ba8
commit
cb724ef6c0
@ -549,6 +549,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):
|
||||
@ -568,6 +578,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):
|
||||
|
@ -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`).
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
@ -71,10 +71,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):
|
||||
@ -82,6 +87,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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user