diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index af8aeb484..3018f7fa9 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -13,6 +13,7 @@ response content is handled by parsers and renderers. from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ValidationError as DjangoValidationError from django.db import models +from django.db.models.query import QuerySet from django.db.models.fields import FieldDoesNotExist from django.utils import six from django.utils.translation import ugettext_lazy as _ @@ -35,6 +36,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: @@ -179,7 +181,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 AttributeError 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: diff --git a/tests/test_serializer.py b/tests/test_serializer.py index 6dabaf42e..7323a7c67 100644 --- a/tests/test_serializer.py +++ b/tests/test_serializer.py @@ -1,3 +1,4 @@ +from django.db.models.query import QuerySet from rest_framework import serializers import pytest @@ -42,6 +43,41 @@ class TestSerializer: with pytest.raises(AttributeError): serializer.data + def test_missing_many_kwarg(self): + 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: def test_non_field_error_validate_method(self):