Catching the error that might arise from not passing many=True to a serializer in the data property instead of the serializer's __init__ method.

This commit is contained in:
Gregor Müllegger 2014-11-17 23:26:07 +01:00
parent 22f059a25a
commit 30ce4c6413
2 changed files with 50 additions and 21 deletions

View File

@ -35,6 +35,7 @@ from rest_framework.validators import (
)
import copy
import inspect
import sys
import warnings
# Note: We do the following so that users of the framework can use this style:
@ -71,7 +72,6 @@ class BaseSerializer(Field):
self.partial = kwargs.pop('partial', False)
self._context = kwargs.pop('context', {})
kwargs.pop('many', None)
kwargs.pop('many_init', None)
super(BaseSerializer, self).__init__(**kwargs)
def __new__(cls, *args, **kwargs):
@ -79,14 +79,6 @@ class BaseSerializer(Field):
# `ListSerializer` classes instead when `many=True` is set.
if kwargs.pop('many', False):
return cls.many_init(*args, **kwargs)
if not kwargs.pop('many_init', False):
if not issubclass(cls, ListSerializer):
instance = kwargs.get('instance', args[0] if args else None)
if isinstance(instance, (list, tuple, QuerySet)):
msg = (
'You have passed a %s as `instance` argument but did '
'not set `many=True`.' % instance.__class__.__name__)
raise AssertionError(msg)
return super(BaseSerializer, cls).__new__(cls, *args, **kwargs)
@classmethod
@ -97,7 +89,7 @@ class BaseSerializer(Field):
control which keyword arguments are passed to the parent, and
which are passed to the child.
"""
child_serializer = cls(many_init=True, *args, **kwargs)
child_serializer = cls(*args, **kwargs)
list_kwargs = {'child': child_serializer}
list_kwargs.update(dict([
(key, value) for key, value in kwargs.items()
@ -169,7 +161,21 @@ class BaseSerializer(Field):
def data(self):
if not hasattr(self, '_data'):
if self.instance is not None and not getattr(self, '_errors', None):
self._data = self.to_representation(self.instance)
try:
self._data = self.to_representation(self.instance)
except Exception as exc:
if isinstance(self.instance, (list, tuple, QuerySet)):
msg = (
'The reason for this exception might be that you '
'have passed a %s as `instance` argument to the '
'serializer but did not set `many=True`.'
% type(self.instance).__name__)
six.reraise(
type(exc),
type(exc)(str(exc) + '. ' + msg),
sys.exc_info()[2])
else:
six.reraise(*sys.exc_info())
elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
self._data = self.to_representation(self.validated_data)
else:

View File

@ -44,16 +44,39 @@ class TestSerializer:
serializer.data
def test_missing_many_kwarg(self):
self.Serializer([], many=True)
with pytest.raises(AssertionError):
self.Serializer([])
with pytest.raises(AssertionError):
self.Serializer(())
with pytest.raises(AssertionError):
self.Serializer(QuerySet())
self.Serializer(None)
self.Serializer(object())
self.Serializer({})
serializer = self.Serializer([], many=True)
assert serializer.data == []
expect_failure = (
(),
[],
QuerySet()
)
for failure in expect_failure:
serializer = self.Serializer(failure)
with pytest.raises(AttributeError):
serializer.data
# Check that message was correctly annotated.
try:
serializer.data
except Exception as exc:
assert 'many=True' in str(exc)
serializer = self.Serializer(None)
assert serializer.data
serializer = self.Serializer(object())
with pytest.raises(AttributeError):
serializer.data
# Check that message was NOT annotated. object() is not list but does
# not has the required `char` and `integer` attributes.
try:
serializer.data
except Exception as exc:
assert 'many=True' not in str(exc)
class TestValidateMethod: