diff options
| author | Tom Christie | 2014-11-06 17:32:33 +0000 | 
|---|---|---|
| committer | Tom Christie | 2014-11-06 17:32:33 +0000 | 
| commit | db1dd8e2406bf54a82d154babd360fba52a0d4bc (patch) | |
| tree | b30e3f59c264b0ac5196fb8f824bd87ddf1f1e56 | |
| parent | 4e001dbb7ac0bc13d6d5fbb4524e905184610aa2 (diff) | |
| download | django-rest-framework-db1dd8e2406bf54a82d154babd360fba52a0d4bc.tar.bz2 | |
Tests & fixes for list serialization
| -rw-r--r-- | rest_framework/utils/html.py | 4 | ||||
| -rw-r--r-- | tests/test_serializer_lists.py | 274 | 
2 files changed, 276 insertions, 2 deletions
| diff --git a/rest_framework/utils/html.py b/rest_framework/utils/html.py index edc591e9..15e83b3b 100644 --- a/rest_framework/utils/html.py +++ b/rest_framework/utils/html.py @@ -2,6 +2,7 @@  Helpers for dealing with HTML input.  """  import re +from django.utils.datastructures import MultiValueDict  def is_html_input(dictionary): @@ -43,7 +44,6 @@ def parse_html_list(dictionary, prefix=''):          {'foo': 'hij', 'bar': 'klm'}      ]      """ -    Dict = type(dictionary)      ret = {}      regex = re.compile(r'^%s\[([0-9]+)\](.*)$' % re.escape(prefix))      for field, value in dictionary.items(): @@ -57,7 +57,7 @@ def parse_html_list(dictionary, prefix=''):          elif isinstance(ret.get(index), dict):              ret[index][key] = value          else: -            ret[index] = Dict({key: value}) +            ret[index] = MultiValueDict({key: [value]})      return [ret[item] for item in sorted(ret.keys())] diff --git a/tests/test_serializer_lists.py b/tests/test_serializer_lists.py new file mode 100644 index 00000000..640067e3 --- /dev/null +++ b/tests/test_serializer_lists.py @@ -0,0 +1,274 @@ +from rest_framework import serializers +from django.utils.datastructures import MultiValueDict + + +class BasicObject: +    """ +    A mock object for testing serializer save behavior. +    """ +    def __init__(self, **kwargs): +        self._data = kwargs +        for key, value in kwargs.items(): +            setattr(self, key, value) + +    def __eq__(self, other): +        if self._data.keys() != other._data.keys(): +            return False +        for key in self._data.keys(): +            if self._data[key] != other._data[key]: +                return False +        return True + + +class TestListSerializer: +    """ +    Tests for using a ListSerializer as a top-level serializer. +    Note that this is in contrast to using ListSerializer as a field. +    """ + +    def setup(self): +        class IntegerListSerializer(serializers.ListSerializer): +            child = serializers.IntegerField() +        self.Serializer = IntegerListSerializer + +    def test_validate(self): +        """ +        Validating a list of items should return a list of validated items. +        """ +        input_data = ["123", "456"] +        expected_output = [123, 456] +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + +    def test_validate_html_input(self): +        """ +        HTML input should be able to mock list structures using [x] style ids. +        """ +        input_data = MultiValueDict({"[0]": ["123"], "[1]": ["456"]}) +        expected_output = [123, 456] +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + + +class TestListSerializerContainingNestedSerializer: +    """ +    Tests for using a ListSerializer containing another serializer. +    """ + +    def setup(self): +        class TestSerializer(serializers.Serializer): +            integer = serializers.IntegerField() +            boolean = serializers.BooleanField() + +            def create(self, validated_data): +                return BasicObject(**validated_data) + +        class ObjectListSerializer(serializers.ListSerializer): +            child = TestSerializer() + +        self.Serializer = ObjectListSerializer + +    def test_validate(self): +        """ +        Validating a list of dictionaries should return a list of +        validated dictionaries. +        """ +        input_data = [ +            {"integer": "123", "boolean": "true"}, +            {"integer": "456", "boolean": "false"} +        ] +        expected_output = [ +            {"integer": 123, "boolean": True}, +            {"integer": 456, "boolean": False} +        ] +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + +    def test_create(self): +        """ +        Creating from a list of dictionaries should return a list of objects. +        """ +        input_data = [ +            {"integer": "123", "boolean": "true"}, +            {"integer": "456", "boolean": "false"} +        ] +        expected_output = [ +            BasicObject(integer=123, boolean=True), +            BasicObject(integer=456, boolean=False), +        ] +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.save() == expected_output + +    def test_serialize(self): +        """ +        Serialization of a list of objects should return a list of dictionaries. +        """ +        input_objects = [ +            BasicObject(integer=123, boolean=True), +            BasicObject(integer=456, boolean=False) +        ] +        expected_output = [ +            {"integer": 123, "boolean": True}, +            {"integer": 456, "boolean": False} +        ] +        serializer = self.Serializer(input_objects) +        assert serializer.data == expected_output + +    def test_validate_html_input(self): +        """ +        HTML input should be able to mock list structures using [x] +        style prefixes. +        """ +        input_data = MultiValueDict({ +            "[0]integer": ["123"], +            "[0]boolean": ["true"], +            "[1]integer": ["456"], +            "[1]boolean": ["false"] +        }) +        expected_output = [ +            {"integer": 123, "boolean": True}, +            {"integer": 456, "boolean": False} +        ] +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + + +class TestNestedListSerializer: +    """ +    Tests for using a ListSerializer as a field. +    """ + +    def setup(self): +        class TestSerializer(serializers.Serializer): +            integers = serializers.ListSerializer(child=serializers.IntegerField()) +            booleans = serializers.ListSerializer(child=serializers.BooleanField()) + +            def create(self, validated_data): +                return BasicObject(**validated_data) + +        self.Serializer = TestSerializer + +    def test_validate(self): +        """ +        Validating a list of items should return a list of validated items. +        """ +        input_data = { +            "integers": ["123", "456"], +            "booleans": ["true", "false"] +        } +        expected_output = { +            "integers": [123, 456], +            "booleans": [True, False] +        } +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + +    def test_create(self): +        """ +        Creation with a list of items return an object with an attribute that +        is a list of items. +        """ +        input_data = { +            "integers": ["123", "456"], +            "booleans": ["true", "false"] +        } +        expected_output = BasicObject( +            integers=[123, 456], +            booleans=[True, False] +        ) +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.save() == expected_output + +    def test_serialize(self): +        """ +        Serialization of a list of items should return a list of items. +        """ +        input_object = BasicObject( +            integers=[123, 456], +            booleans=[True, False] +        ) +        expected_output = { +            "integers": [123, 456], +            "booleans": [True, False] +        } +        serializer = self.Serializer(input_object) +        assert serializer.data == expected_output + +    def test_validate_html_input(self): +        """ +        HTML input should be able to mock list structures using [x] +        style prefixes. +        """ +        input_data = MultiValueDict({ +            "integers[0]": ["123"], +            "integers[1]": ["456"], +            "booleans[0]": ["true"], +            "booleans[1]": ["false"] +        }) +        expected_output = { +            "integers": [123, 456], +            "booleans": [True, False] +        } +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + + +class TestNestedListOfListsSerializer: +    def setup(self): +        class TestSerializer(serializers.Serializer): +            integers = serializers.ListSerializer( +                child=serializers.ListSerializer( +                    child=serializers.IntegerField() +                ) +            ) +            booleans = serializers.ListSerializer( +                child=serializers.ListSerializer( +                    child=serializers.BooleanField() +                ) +            ) + +        self.Serializer = TestSerializer + +    def test_validate(self): +        input_data = { +            'integers': [['123', '456'], ['789', '0']], +            'booleans': [['true', 'true'], ['false', 'true']] +        } +        expected_output = { +            "integers": [[123, 456], [789, 0]], +            "booleans": [[True, True], [False, True]] +        } +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output + +    def test_validate_html_input(self): +        """ +        HTML input should be able to mock lists of lists using [x][y] +        style prefixes. +        """ +        input_data = MultiValueDict({ +            "integers[0][0]": ["123"], +            "integers[0][1]": ["456"], +            "integers[1][0]": ["789"], +            "integers[1][1]": ["000"], +            "booleans[0][0]": ["true"], +            "booleans[0][1]": ["true"], +            "booleans[1][0]": ["false"], +            "booleans[1][1]": ["true"] +        }) +        expected_output = { +            "integers": [[123, 456], [789, 0]], +            "booleans": [[True, True], [False, True]] +        } +        serializer = self.Serializer(data=input_data) +        assert serializer.is_valid() +        assert serializer.validated_data == expected_output | 
