Check for collection.Mapping instead of dict

issue #4901
This commit is contained in:
Isaac Stone 2017-02-19 13:00:41 -08:00
parent 300e46a9e5
commit 289e1e440e
2 changed files with 36 additions and 5 deletions

View File

@ -15,7 +15,7 @@ from __future__ import unicode_literals
import copy import copy
import inspect import inspect
import traceback import traceback
from collections import OrderedDict from collections import Mapping, OrderedDict
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@ -326,11 +326,11 @@ def as_serializer_error(exc):
else: else:
detail = exc.detail 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}. # 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. # Here we ensure that all the values are *lists* of errors.
return { 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() for key, value in detail.items()
} }
elif isinstance(detail, list): elif isinstance(detail, list):
@ -442,7 +442,7 @@ class Serializer(BaseSerializer):
""" """
Dict of native values <- Dict of primitive datatypes. Dict of native values <- Dict of primitive datatypes.
""" """
if not isinstance(data, dict): if not isinstance(data, Mapping):
message = self.error_messages['invalid'].format( message = self.error_messages['invalid'].format(
datatype=type(data).__name__ datatype=type(data).__name__
) )

View File

@ -4,9 +4,10 @@ from __future__ import unicode_literals
import inspect import inspect
import pickle import pickle
import re import re
import unittest
from collections import Mapping
import pytest import pytest
from django.db import models from django.db import models
from rest_framework import fields, relations, serializers from rest_framework import fields, relations, serializers
@ -15,6 +16,11 @@ from rest_framework.fields import Field
from .utils import MockObject from .utils import MockObject
try:
from collections import ChainMap
except ImportError:
ChainMap = False
# Test serializer fields imports. # Test serializer fields imports.
# ------------------------------- # -------------------------------
@ -113,6 +119,31 @@ class TestSerializer:
assert not serializer.is_valid() assert not serializer.is_valid()
assert serializer.errors == {'non_field_errors': ['No data provided']} 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: class TestValidateMethod:
def test_non_field_error_validate_method(self): def test_non_field_error_validate_method(self):