mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	[4.0.x] Fixed #33333 -- Fixed setUpTestData() crash with models.BinaryField on PostgreSQL.
This makes models.BinaryField pickleable on PostgreSQL. Regression in3cf80d3fcf. Thanks Adam Zimmerman for the report. Backport of2c7846d992from main.
This commit is contained in:
		| @@ -553,6 +553,16 @@ class Model(metaclass=ModelBase): | |||||||
|         state = self.__dict__.copy() |         state = self.__dict__.copy() | ||||||
|         state['_state'] = copy.copy(state['_state']) |         state['_state'] = copy.copy(state['_state']) | ||||||
|         state['_state'].fields_cache = state['_state'].fields_cache.copy() |         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 |         return state | ||||||
|  |  | ||||||
|     def __setstate__(self, state): |     def __setstate__(self, state): | ||||||
| @@ -572,6 +582,9 @@ class Model(metaclass=ModelBase): | |||||||
|                 RuntimeWarning, |                 RuntimeWarning, | ||||||
|                 stacklevel=2, |                 stacklevel=2, | ||||||
|             ) |             ) | ||||||
|  |         if '_memoryview_attrs' in state: | ||||||
|  |             for attr, value in state.pop('_memoryview_attrs'): | ||||||
|  |                 state[attr] = memoryview(value) | ||||||
|         self.__dict__.update(state) |         self.__dict__.update(state) | ||||||
|  |  | ||||||
|     def _get_pk_val(self, meta=None): |     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 | 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) |     number1 = models.IntegerField(blank=True, default=standalone_number) | ||||||
|     number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) |     number2 = models.IntegerField(blank=True, default=Numbers.get_static_number) | ||||||
|     event = models.OneToOneField(Event, models.CASCADE, null=True) |     event = models.OneToOneField(Event, models.CASCADE, null=True) | ||||||
|  |     data = models.BinaryField(null=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Container: | class Container: | ||||||
|   | |||||||
| @@ -16,6 +16,10 @@ class PickleabilityTestCase(TestCase): | |||||||
|     def assert_pickles(self, qs): |     def assert_pickles(self, qs): | ||||||
|         self.assertEqual(list(pickle.loads(pickle.dumps(qs))), list(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): |     def test_related_field(self): | ||||||
|         g = Group.objects.create(name="Ponies Who Own Maybachs") |         g = Group.objects.create(name="Ponies Who Own Maybachs") | ||||||
|         self.assert_pickles(Event.objects.filter(group=g.id)) |         self.assert_pickles(Event.objects.filter(group=g.id)) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ class Car(models.Model): | |||||||
| class Person(models.Model): | class Person(models.Model): | ||||||
|     name = models.CharField(max_length=100) |     name = models.CharField(max_length=100) | ||||||
|     cars = models.ManyToManyField(Car, through='PossessedCar') |     cars = models.ManyToManyField(Car, through='PossessedCar') | ||||||
|  |     data = models.BinaryField(null=True) | ||||||
|  |  | ||||||
|  |  | ||||||
| class PossessedCar(models.Model): | class PossessedCar(models.Model): | ||||||
|   | |||||||
| @@ -81,10 +81,15 @@ class TestDataTests(TestCase): | |||||||
|         ) |         ) | ||||||
|         cls.non_deepcopy_able = NonDeepCopyAble() |         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 |     @assert_no_queries | ||||||
|     def test_class_attribute_equality(self): |     def test_class_attribute_equality(self): | ||||||
|         """Class level test data is equal to instance level test data.""" |         """Class level test data is equal to instance level test data.""" | ||||||
|         self.assertEqual(self.jim_douglas, self.__class__.jim_douglas) |         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 |     @assert_no_queries | ||||||
|     def test_class_attribute_identity(self): |     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. |         Class level test data is not identical to instance level test data. | ||||||
|         """ |         """ | ||||||
|         self.assertIsNot(self.jim_douglas, self.__class__.jim_douglas) |         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 |     @assert_no_queries | ||||||
|     def test_identity_preservation(self): |     def test_identity_preservation(self): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user