diff --git a/rest_framework/class_validator.py b/rest_framework/class_validator.py new file mode 100644 index 000000000..68d3d9ba3 --- /dev/null +++ b/rest_framework/class_validator.py @@ -0,0 +1,41 @@ +"""Attribute checking for Python classes +""" + +class InvalidAttributeError(AttributeError): + """ + Use this for invalid attributes because AttributeError means not found + """ + + pass + + +class ValidatorMeta(type): + """ + Metaclass to guard against setting unrecognized attributes + Set it as __metaclass__ as low in the inheritance chain as possible + """ + def __new__(cls, name, parents, kwargs): + """Creates the new class, and sees if it has only known attributes + """ + new_cls = type.__new__(cls, name, parents, kwargs) + + ## Fail only if the new class defines an api + if not new_cls.__dict__.has_key('valid_attributes'): + return new_cls + + ## Do some sanity checks to not fail in vain + for attr, val in kwargs.items(): + if attr == 'valid_attributes': + continue + if attr.startswith('_'): + continue + # Methods are always allowed + if callable(val): + continue + + # Ensure validity + if not attr in new_cls.valid_attributes: + raise InvalidAttributeError(attr) + + return new_cls + diff --git a/rest_framework/tests/views.py b/rest_framework/tests/views.py index 994cf6dc3..eb0a78f65 100644 --- a/rest_framework/tests/views.py +++ b/rest_framework/tests/views.py @@ -12,6 +12,10 @@ factory = RequestFactory() class BasicView(APIView): + ## Uncomment this to see how attribute validation works + # valid_attributes = ('foo', ) + # foo = 'foo' + # bar = 'bar' def get(self, request, *args, **kwargs): return Response({'method': 'GET'}) diff --git a/rest_framework/views.py b/rest_framework/views.py index 555fa2f40..343292e97 100644 --- a/rest_framework/views.py +++ b/rest_framework/views.py @@ -10,10 +10,19 @@ from rest_framework.compat import View from rest_framework.response import Response from rest_framework.request import Request from rest_framework.settings import api_settings +from rest_framework.class_validator import ValidatorMeta from rest_framework.utils.formatting import get_view_name, get_view_description class APIView(View): + __metaclass__ = ValidatorMeta + valid_attributes = set(('settings', 'renderer_classes', 'parser_classes', + 'authentication_classes', 'throttle_classes', + 'permission_classes', 'content_negotiation_class', + 'allowed_methods', 'default_response_headers', + 'as_view', + )) + settings = api_settings renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES