From bef4408d102df061b4fe9352d8a7378ee22ff3ba Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 1 Jul 2015 14:43:18 +0300 Subject: [PATCH 1/5] Added MappingField --- rest_framework/fields.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 7960295bd..55fb2aac8 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1165,6 +1165,35 @@ class MultipleChoiceField(ChoiceField): ]) +# Mapping types... + +class MappingField(Field): + default_error_messages = { + 'key_not_found': _('"{value}" not found in "mapping" dict'), + 'value_not_found': _('"{value}" not found in "mapping" dict') + } + + def __init__(self, mapping, **kwargs): + super(MappingField, self).__init__(**kwargs) + + assert isinstance(mapping, dict), '"mapping" should be a dictionary' + for k, v in mapping.items(): + assert isinstance(k, (str, int)) and isinstance(v, (str, int)), '"mapping" can contain only str or int' + + self.mapping = mapping + self.reverse_mapping = {v: k for k, v in mapping.iteritems()} + + def to_representation(self, value): + if value in self.mapping: + return self.mapping[value] + self.fail('key_not_found', value=value) + + def to_internal_value(self, data): + if data in self.reverse_mapping: + return self.reverse_mapping[data] + self.fail('value_not_found', value=data) + + # File types... class FileField(Field): From 806b0f60cf985ac26944d86b31f5a3211762182c Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 1 Jul 2015 14:43:35 +0300 Subject: [PATCH 2/5] Added test for MappingField --- tests/test_fields.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/test_fields.py b/tests/test_fields.py index 76e6d9d60..f4ef749aa 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1125,6 +1125,38 @@ class TestMultipleChoiceField(FieldValues): assert field.get_value(QueryDict({})) == rest_framework.fields.empty +# Mapping types... + +class TestMappingFieldWithIntKeys(FieldValues): + """ + Valid and invalid values for `MappingField`. + """ + valid_inputs = { + 1: 'one', + 2: 'two', + 'three': 3, + 'four': 4 + } + invalid_inputs = { + 5: ['"5" not found in "mapping" dict'], + 'abc': ['"abc" not found in "mapping" dict'] + } + outputs = { + 'one': 1, + 'two': 2, + 3: 'three', + 4: 'four' + } + field = serializers.MappingField( + mapping={ + 1: 'one', + 2: 'two', + 'three': 3, + 'four': 4 + } + ) + + # File serializers... class MockFile: From 643893334b6cde46274755f86da667068961df7d Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 1 Jul 2015 14:43:50 +0300 Subject: [PATCH 3/5] Added docs for MappingField --- docs/api-guide/fields.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 18637f21c..b335a2b1a 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -364,6 +364,18 @@ As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid --- +# Mapping field + +## MappingField + +A field that converts value with given mapping dict. + + **Signature:** `MappingField(mapping)` + +- `mapping` - A dict of valid values that should be converted both directions. + +--- + # File upload fields #### Parsers and file uploads. From 34232b01994c67b3e6c60a9102c7940caff81626 Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 1 Jul 2015 15:16:28 +0300 Subject: [PATCH 4/5] Changed MappingField default error messages --- rest_framework/fields.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rest_framework/fields.py b/rest_framework/fields.py index 55fb2aac8..ff771128d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -1169,8 +1169,8 @@ class MultipleChoiceField(ChoiceField): class MappingField(Field): default_error_messages = { - 'key_not_found': _('"{value}" not found in "mapping" dict'), - 'value_not_found': _('"{value}" not found in "mapping" dict') + 'key_not_found': _('"{value}" not found in "mapping" keys'), + 'value_not_found': _('"{value}" not found in "mapping" values') } def __init__(self, mapping, **kwargs): From 42dca62e7733bb095435b804061fbffe89f9de7a Mon Sep 17 00:00:00 2001 From: Anton-Shutik Date: Wed, 1 Jul 2015 15:16:57 +0300 Subject: [PATCH 5/5] Fixed MappingField tests --- tests/test_fields.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_fields.py b/tests/test_fields.py index f4ef749aa..c13dc4c61 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1132,21 +1132,21 @@ class TestMappingFieldWithIntKeys(FieldValues): Valid and invalid values for `MappingField`. """ valid_inputs = { - 1: 'one', - 2: 'two', - 'three': 3, - 'four': 4 - } - invalid_inputs = { - 5: ['"5" not found in "mapping" dict'], - 'abc': ['"abc" not found in "mapping" dict'] - } - outputs = { 'one': 1, 'two': 2, 3: 'three', 4: 'four' } + invalid_inputs = { + 5: ['"5" not found in "mapping" values'], + 'abc': ['"abc" not found in "mapping" values'] + } + outputs = { + 1: 'one', + 2: 'two', + 'three': 3, + 'four': 4 + } field = serializers.MappingField( mapping={ 1: 'one',