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.
This commit is contained in:
parent
b09b71bf34
commit
928c12eb1a
|
@ -106,6 +106,9 @@ class ArrayField(Field):
|
||||||
base_field = self.base_field
|
base_field = self.base_field
|
||||||
|
|
||||||
for val in vals:
|
for val in vals:
|
||||||
|
if val is None:
|
||||||
|
values.append(None)
|
||||||
|
else:
|
||||||
obj = AttributeSetter(base_field.attname, val)
|
obj = AttributeSetter(base_field.attname, val)
|
||||||
values.append(base_field.value_to_string(obj))
|
values.append(base_field.value_to_string(obj))
|
||||||
return json.dumps(values)
|
return json.dumps(values)
|
||||||
|
|
|
@ -51,7 +51,11 @@ 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)
|
||||||
|
if val is None:
|
||||||
|
result[end] = None
|
||||||
|
else:
|
||||||
|
obj = AttributeSetter(base_field.attname, val)
|
||||||
result[end] = base_field.value_to_string(obj)
|
result[end] = base_field.value_to_string(obj)
|
||||||
return json.dumps(result)
|
return json.dumps(result)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
* 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):
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
|
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
@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):
|
||||||
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):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue