From 289e1e440ef6b18ffcfbc74823d69af61ea97529 Mon Sep 17 00:00:00 2001 From: Isaac Stone Date: Sun, 19 Feb 2017 13:00:41 -0800 Subject: [PATCH] Check for collection.Mapping instead of dict issue #4901 --- rest_framework/serializers.py | 8 ++++---- tests/test_serializer.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index a8f6e9df2..80e384c22 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -15,7 +15,7 @@ from __future__ import unicode_literals import copy import inspect import traceback -from collections import OrderedDict +from collections import Mapping, OrderedDict from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ImproperlyConfigured @@ -326,11 +326,11 @@ def as_serializer_error(exc): else: detail = exc.detail - if isinstance(detail, dict): + if isinstance(detail, Mapping): # If errors may be a dict we use the standard {key: list of values}. # Here we ensure that all the values are *lists* of errors. return { - key: value if isinstance(value, (list, dict)) else [value] + key: value if isinstance(value, (list, Mapping)) else [value] for key, value in detail.items() } elif isinstance(detail, list): @@ -442,7 +442,7 @@ class Serializer(BaseSerializer): """ Dict of native values <- Dict of primitive datatypes. """ - if not isinstance(data, dict): + if not isinstance(data, Mapping): message = self.error_messages['invalid'].format( datatype=type(data).__name__ ) diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 04fa39c0d..cd82ba3df 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -4,9 +4,10 @@ from __future__ import unicode_literals import inspect import pickle import re +import unittest +from collections import Mapping import pytest - from django.db import models from rest_framework import fields, relations, serializers @@ -15,6 +16,11 @@ from rest_framework.fields import Field from .utils import MockObject +try: + from collections import ChainMap +except ImportError: + ChainMap = False + # Test serializer fields imports. # ------------------------------- @@ -113,6 +119,31 @@ class TestSerializer: assert not serializer.is_valid() assert serializer.errors == {'non_field_errors': ['No data provided']} + @unittest.skipUnless(ChainMap, 'requires python 3.3') + def test_serialize_chainmap(self): + data = ChainMap({'char': 'abc'}, {'integer': 123}) + serializer = self.Serializer(data=data) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.errors == {} + + def test_serialize_custom_mapping(self): + class SinglePurposeMapping(Mapping): + def __getitem__(self, key): + return 'abc' if key == 'char' else 123 + + def __iter__(self): + yield 'char' + yield 'integer' + + def __len__(self): + return 2 + + serializer = self.Serializer(data=SinglePurposeMapping()) + assert serializer.is_valid() + assert serializer.validated_data == {'char': 'abc', 'integer': 123} + assert serializer.errors == {} + class TestValidateMethod: def test_non_field_error_validate_method(self):