From 6fda742b890e7fc86250fb0c55bbd75fb828f652 Mon Sep 17 00:00:00 2001 From: Ryan P Kilby Date: Tue, 5 Dec 2017 00:35:48 -0500 Subject: [PATCH] Add HStoreField --- rest_framework/fields.py | 4 ++++ rest_framework/serializers.py | 11 ++++------- tests/test_fields.py | 34 ++++++++++++++++++++++++++++++++++ tests/test_model_serializer.py | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index a710df7b4..b39f394f6 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1684,6 +1684,10 @@ class DictField(Field): } +class HStoreField(DictField): + child = CharField(allow_blank=True, allow_null=True) + + class JSONField(Field): default_error_messages = { 'invalid': _('Value must be valid JSON.') diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index e2ea0d744..69341d51e 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -54,9 +54,9 @@ from rest_framework.validators import ( from rest_framework.fields import ( # NOQA # isort:skip BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField, DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField, - HiddenField, IPAddressField, ImageField, IntegerField, JSONField, ListField, - ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, RegexField, - SerializerMethodField, SlugField, TimeField, URLField, UUIDField, + HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField, + ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, + RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField, ) from rest_framework.relations import ( # NOQA # isort:skip HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField, @@ -1541,10 +1541,7 @@ if hasattr(models, 'IPAddressField'): ModelSerializer.serializer_field_mapping[models.IPAddressField] = IPAddressField if postgres_fields: - class CharMappingField(DictField): - child = CharField(allow_blank=True) - - ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField + ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = HStoreField ModelSerializer.serializer_field_mapping[postgres_fields.ArrayField] = ListField ModelSerializer.serializer_field_mapping[postgres_fields.JSONField] = JSONField diff --git a/tests/test_fields.py b/tests/test_fields.py index fc9ce192a..88bf21678 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1897,6 +1897,40 @@ class TestUnvalidatedDictField(FieldValues): field = serializers.DictField() +class TestHStoreField(FieldValues): + """ + Values for `ListField` with CharField as child. + """ + valid_inputs = [ + ({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}), + ({'a': 1, 'b': None}, {'a': '1', 'b': None}), + ] + invalid_inputs = [ + ('not a dict', ['Expected a dictionary of items but got type "str".']), + ] + outputs = [ + ({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}), + ] + field = serializers.HStoreField() + + def test_no_source_on_child(self): + with pytest.raises(AssertionError) as exc_info: + serializers.HStoreField(child=serializers.CharField(source='other')) + + assert str(exc_info.value) == ( + "The `source` argument is not meaningful when applied to a `child=` field. " + "Remove `source=` from the field declaration." + ) + + def test_allow_null(self): + """ + If `allow_null=True` then `None` is a valid input. + """ + field = serializers.HStoreField(allow_null=True) + output = field.run_validation(None) + assert output is None + + class TestJSONField(FieldValues): """ Values for `JSONField`. diff --git a/tests/test_model_serializer.py b/tests/test_model_serializer.py index 72011c447..e55afe03e 100644 --- a/tests/test_model_serializer.py +++ b/tests/test_model_serializer.py @@ -392,7 +392,7 @@ class TestPosgresFieldsMapping(TestCase): expected = dedent(""" TestSerializer(): - hstore_field = CharMappingField() + hstore_field = HStoreField() """) self.assertEqual(unicode_repr(TestSerializer()), expected)