Kaydet (Commit) 928c12eb authored tarafından Claude Paroz's avatar Claude Paroz

Fixed #26215 -- Fixed RangeField/ArrayField serialization with None values

Also added tests for HStoreField and JSONField.
Thanks Aleksey Bukin for the report and Tim Graham for the initial patch and
the review.
üst b09b71bf
...@@ -106,8 +106,11 @@ class ArrayField(Field): ...@@ -106,8 +106,11 @@ class ArrayField(Field):
base_field = self.base_field base_field = self.base_field
for val in vals: for val in vals:
obj = AttributeSetter(base_field.attname, val) if val is None:
values.append(base_field.value_to_string(obj)) values.append(None)
else:
obj = AttributeSetter(base_field.attname, val)
values.append(base_field.value_to_string(obj))
return json.dumps(values) return json.dumps(values)
def get_transform(self, name): def get_transform(self, name):
......
...@@ -51,8 +51,12 @@ class RangeField(models.Field): ...@@ -51,8 +51,12 @@ class RangeField(models.Field):
base_field = self.base_field base_field = self.base_field
result = {"bounds": value._bounds} result = {"bounds": value._bounds}
for end in ('lower', 'upper'): for end in ('lower', 'upper'):
obj = AttributeSetter(base_field.attname, getattr(value, end)) val = getattr(value, end)
result[end] = base_field.value_to_string(obj) if val is None:
result[end] = None
else:
obj = AttributeSetter(base_field.attname, val)
result[end] = base_field.value_to_string(obj)
return json.dumps(result) return json.dumps(result)
def formfield(self, **kwargs): def formfield(self, **kwargs):
......
...@@ -17,3 +17,7 @@ Bugfixes ...@@ -17,3 +17,7 @@ Bugfixes
* Made ``forms.FileField`` and ``utils.translation.lazy_number()`` picklable * Made ``forms.FileField`` and ``utils.translation.lazy_number()`` picklable
(:ticket:`26212`). (:ticket:`26212`).
* Fixed :class:`~django.contrib.postgres.fields.RangeField` and
:class:`~django.contrib.postgres.fields.ArrayField` serialization with
``None`` values (:ticket:`26215`).
...@@ -27,3 +27,7 @@ Bugfixes ...@@ -27,3 +27,7 @@ Bugfixes
* Made ``forms.FileField`` and ``utils.translation.lazy_number()`` picklable * Made ``forms.FileField`` and ``utils.translation.lazy_number()`` picklable
(:ticket:`26212`). (:ticket:`26212`).
* Fixed :class:`~django.contrib.postgres.fields.RangeField` and
:class:`~django.contrib.postgres.fields.ArrayField` serialization with
``None`` values (:ticket:`26215`).
...@@ -448,17 +448,17 @@ class TestMigrations(TransactionTestCase): ...@@ -448,17 +448,17 @@ class TestMigrations(TransactionTestCase):
class TestSerialization(PostgreSQLTestCase): class TestSerialization(PostgreSQLTestCase):
test_data = ( test_data = (
'[{"fields": {"field": "[\\"1\\", \\"2\\"]"}, "model": "postgres_tests.integerarraymodel", "pk": null}]' '[{"fields": {"field": "[\\"1\\", \\"2\\", null]"}, "model": "postgres_tests.integerarraymodel", "pk": null}]'
) )
def test_dumping(self): def test_dumping(self):
instance = IntegerArrayModel(field=[1, 2]) instance = IntegerArrayModel(field=[1, 2, None])
data = serializers.serialize('json', [instance]) data = serializers.serialize('json', [instance])
self.assertEqual(json.loads(data), json.loads(self.test_data)) self.assertEqual(json.loads(data), json.loads(self.test_data))
def test_loading(self): def test_loading(self):
instance = list(serializers.deserialize('json', self.test_data))[0].object instance = list(serializers.deserialize('json', self.test_data))[0].object
self.assertEqual(instance.field, [1, 2]) self.assertEqual(instance.field, [1, 2, None])
class TestValidation(PostgreSQLTestCase): class TestValidation(PostgreSQLTestCase):
......
...@@ -165,7 +165,8 @@ class TestQuerying(PostgreSQLTestCase): ...@@ -165,7 +165,8 @@ class TestQuerying(PostgreSQLTestCase):
class TestSerialization(PostgreSQLTestCase): class TestSerialization(PostgreSQLTestCase):
test_data = '[{"fields": {"field": "{\\"a\\": \\"b\\"}"}, "model": "postgres_tests.hstoremodel", "pk": null}]' test_data = ('[{"fields": {"field": "{\\"a\\": \\"b\\"}"}, '
'"model": "postgres_tests.hstoremodel", "pk": null}]')
def test_dumping(self): def test_dumping(self):
instance = HStoreModel(field={'a': 'b'}) instance = HStoreModel(field={'a': 'b'})
...@@ -176,6 +177,12 @@ class TestSerialization(PostgreSQLTestCase): ...@@ -176,6 +177,12 @@ class TestSerialization(PostgreSQLTestCase):
instance = list(serializers.deserialize('json', self.test_data))[0].object instance = list(serializers.deserialize('json', self.test_data))[0].object
self.assertEqual(instance.field, {'a': 'b'}) self.assertEqual(instance.field, {'a': 'b'})
def test_roundtrip_with_null(self):
instance = HStoreModel(field={'a': 'b', 'c': None})
data = serializers.serialize('json', [instance])
new_instance = list(serializers.deserialize('json', data))[0].object
self.assertEqual(instance.field, new_instance.field)
class TestValidation(PostgreSQLTestCase): class TestValidation(PostgreSQLTestCase):
......
...@@ -213,16 +213,16 @@ class TestQuerying(TestCase): ...@@ -213,16 +213,16 @@ class TestQuerying(TestCase):
@skipUnlessPG94 @skipUnlessPG94
class TestSerialization(TestCase): class TestSerialization(TestCase):
test_data = '[{"fields": {"field": {"a": "b"}}, "model": "postgres_tests.jsonmodel", "pk": null}]' test_data = '[{"fields": {"field": {"a": "b", "c": null}}, "model": "postgres_tests.jsonmodel", "pk": null}]'
def test_dumping(self): def test_dumping(self):
instance = JSONModel(field={'a': 'b'}) instance = JSONModel(field={'a': 'b', 'c': None})
data = serializers.serialize('json', [instance]) data = serializers.serialize('json', [instance])
self.assertJSONEqual(data, self.test_data) self.assertJSONEqual(data, self.test_data)
def test_loading(self): def test_loading(self):
instance = list(serializers.deserialize('json', self.test_data))[0].object instance = list(serializers.deserialize('json', self.test_data))[0].object
self.assertEqual(instance.field, {'a': 'b'}) self.assertEqual(instance.field, {'a': 'b', 'c': None})
class TestValidation(PostgreSQLTestCase): class TestValidation(PostgreSQLTestCase):
......
...@@ -309,6 +309,17 @@ class TestSerialization(PostgreSQLTestCase): ...@@ -309,6 +309,17 @@ class TestSerialization(PostgreSQLTestCase):
self.assertEqual(instance.dates, DateRange(self.lower_date, self.upper_date)) self.assertEqual(instance.dates, DateRange(self.lower_date, self.upper_date))
self.assertEqual(instance.timestamps, DateTimeTZRange(self.lower_dt, self.upper_dt)) self.assertEqual(instance.timestamps, DateTimeTZRange(self.lower_dt, self.upper_dt))
def test_serialize_range_with_null(self):
instance = RangesModel(ints=NumericRange(None, 10))
data = serializers.serialize('json', [instance])
new_instance = list(serializers.deserialize('json', data))[0].object
self.assertEqual(new_instance.ints, NumericRange(None, 10))
instance = RangesModel(ints=NumericRange(10, None))
data = serializers.serialize('json', [instance])
new_instance = list(serializers.deserialize('json', data))[0].object
self.assertEqual(new_instance.ints, NumericRange(10, None))
class TestValidators(PostgreSQLTestCase): class TestValidators(PostgreSQLTestCase):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment