diff --git a/djangorestframework/mixins.py b/djangorestframework/mixins.py index 622575923..90c759708 100644 --- a/djangorestframework/mixins.py +++ b/djangorestframework/mixins.py @@ -216,7 +216,7 @@ class RequestMixin(object): class ResponseMixin(object): """ - Adds behavior for pluggable `Renderers` to a :class:`views.BaseView` or Django :class:`View` class. + Adds behavior for pluggable `Renderers` to a :class:`views.View` class. Default behavior is to use standard HTTP Accept header content negotiation. Also supports overriding the content type by specifying an ``_accept=`` parameter in the URL. diff --git a/djangorestframework/tests/accept.py b/djangorestframework/tests/accept.py index 293a72847..f26de633f 100644 --- a/djangorestframework/tests/accept.py +++ b/djangorestframework/tests/accept.py @@ -1,6 +1,6 @@ from django.test import TestCase from djangorestframework.compat import RequestFactory -from djangorestframework.views import BaseView +from djangorestframework.views import View # See: http://www.useragentstring.com/ @@ -19,7 +19,7 @@ class UserAgentMungingTest(TestCase): def setUp(self): - class MockView(BaseView): + class MockView(View): permissions = () def get(self, request): diff --git a/djangorestframework/tests/authentication.py b/djangorestframework/tests/authentication.py index 04ac471ad..8254403c4 100644 --- a/djangorestframework/tests/authentication.py +++ b/djangorestframework/tests/authentication.py @@ -6,13 +6,13 @@ from django.test import Client, TestCase from django.utils import simplejson as json from djangorestframework.compat import RequestFactory -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework import permissions import base64 -class MockView(BaseView): +class MockView(View): permissions = ( permissions.IsAuthenticated, ) def post(self, request): return {'a':1, 'b':2, 'c':3} diff --git a/djangorestframework/tests/breadcrumbs.py b/djangorestframework/tests/breadcrumbs.py index 1fd756347..158f18008 100644 --- a/djangorestframework/tests/breadcrumbs.py +++ b/djangorestframework/tests/breadcrumbs.py @@ -1,21 +1,21 @@ from django.conf.urls.defaults import patterns, url from django.test import TestCase from djangorestframework.utils.breadcrumbs import get_breadcrumbs -from djangorestframework.views import BaseView +from djangorestframework.views import View -class Root(BaseView): +class Root(View): pass -class ResourceRoot(BaseView): +class ResourceRoot(View): pass -class ResourceInstance(BaseView): +class ResourceInstance(View): pass -class NestedResourceRoot(BaseView): +class NestedResourceRoot(View): pass -class NestedResourceInstance(BaseView): +class NestedResourceInstance(View): pass urlpatterns = patterns('', diff --git a/djangorestframework/tests/description.py b/djangorestframework/tests/description.py index 6c494efaa..1ce291124 100644 --- a/djangorestframework/tests/description.py +++ b/djangorestframework/tests/description.py @@ -1,5 +1,5 @@ from django.test import TestCase -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.compat import apply_markdown from djangorestframework.utils.description import get_name, get_description @@ -35,7 +35,7 @@ MARKED_DOWN = """

an example docstring

class TestViewNamesAndDescriptions(TestCase): def test_resource_name_uses_classname_by_default(self): """Ensure Resource names are based on the classname by default.""" - class MockView(BaseView): + class MockView(View): pass self.assertEquals(get_name(MockView()), 'Mock') @@ -43,13 +43,13 @@ class TestViewNamesAndDescriptions(TestCase): #def test_resource_name_can_be_set_explicitly(self): # """Ensure Resource names can be set using the 'name' class attribute.""" # example = 'Some Other Name' - # class MockView(BaseView): + # class MockView(View): # name = example # self.assertEquals(get_name(MockView()), example) def test_resource_description_uses_docstring_by_default(self): """Ensure Resource names are based on the docstring by default.""" - class MockView(BaseView): + class MockView(View): """an example docstring ==================== @@ -71,7 +71,7 @@ class TestViewNamesAndDescriptions(TestCase): #def test_resource_description_can_be_set_explicitly(self): # """Ensure Resource descriptions can be set using the 'description' class attribute.""" # example = 'Some other description' - # class MockView(BaseView): + # class MockView(View): # """docstring""" # description = example # self.assertEquals(get_description(MockView()), example) @@ -79,13 +79,13 @@ class TestViewNamesAndDescriptions(TestCase): #def test_resource_description_does_not_require_docstring(self): # """Ensure that empty docstrings do not affect the Resource's description if it has been set using the 'description' class attribute.""" # example = 'Some other description' - # class MockView(BaseView): + # class MockView(View): # description = example # self.assertEquals(get_description(MockView()), example) def test_resource_description_can_be_empty(self): """Ensure that if a resource has no doctring or 'description' class attribute, then it's description is the empty string""" - class MockView(BaseView): + class MockView(View): pass self.assertEquals(get_description(MockView()), '') diff --git a/djangorestframework/tests/files.py b/djangorestframework/tests/files.py index 3892422c1..25aad9b49 100644 --- a/djangorestframework/tests/files.py +++ b/djangorestframework/tests/files.py @@ -1,7 +1,7 @@ from django.test import TestCase from django import forms from djangorestframework.compat import RequestFactory -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.resources import FormResource import StringIO @@ -19,7 +19,7 @@ class UploadFilesTests(TestCase): class MockResource(FormResource): form = FileForm - class MockView(BaseView): + class MockView(View): permissions = () resource = MockResource diff --git a/djangorestframework/tests/parsers.py b/djangorestframework/tests/parsers.py index 764a8f5cb..3ab1a61c8 100644 --- a/djangorestframework/tests/parsers.py +++ b/djangorestframework/tests/parsers.py @@ -2,11 +2,11 @@ # .. # >>> from djangorestframework.parsers import FormParser # >>> from djangorestframework.compat import RequestFactory -# >>> from djangorestframework.views import BaseView +# >>> from djangorestframework.views import View # >>> from StringIO import StringIO # >>> from urllib import urlencode # >>> req = RequestFactory().get('/') -# >>> some_view = BaseView() +# >>> some_view = View() # >>> some_view.request = req # Make as if this request had been dispatched # # FormParser @@ -85,7 +85,7 @@ # from django.test import TestCase # from djangorestframework.compat import RequestFactory # from djangorestframework.parsers import MultiPartParser -# from djangorestframework.views import BaseView +# from djangorestframework.views import View # from StringIO import StringIO # # def encode_multipart_formdata(fields, files): @@ -125,7 +125,7 @@ # def test_multipartparser(self): # """Ensure that MultiPartParser can parse multipart/form-data that contains a mix of several files and parameters.""" # post_req = RequestFactory().post('/', self.body, content_type=self.content_type) -# view = BaseView() +# view = View() # view.request = post_req # (data, files) = MultiPartParser(view).parse(StringIO(self.body)) # self.assertEqual(data['key1'], 'val1') diff --git a/djangorestframework/tests/renderers.py b/djangorestframework/tests/renderers.py index fcc405a19..5364cd2e9 100644 --- a/djangorestframework/tests/renderers.py +++ b/djangorestframework/tests/renderers.py @@ -1,7 +1,7 @@ from django.conf.urls.defaults import patterns, url from django import http from django.test import TestCase -from djangorestframework.compat import View +from djangorestframework.compat import View as DjangoView from djangorestframework.renderers import BaseRenderer, JSONRenderer from djangorestframework.mixins import ResponseMixin from djangorestframework.response import Response @@ -13,7 +13,7 @@ DUMMYCONTENT = 'dummycontent' RENDERER_A_SERIALIZER = lambda x: 'Renderer A: %s' % x RENDERER_B_SERIALIZER = lambda x: 'Renderer B: %s' % x -class MockView(ResponseMixin, View): +class MockView(ResponseMixin, DjangoView): def get(self, request): response = Response(DUMMYSTATUS, DUMMYCONTENT) return self.render(response) diff --git a/djangorestframework/tests/reverse.py b/djangorestframework/tests/reverse.py index 7026d4a47..b4b0a7938 100644 --- a/djangorestframework/tests/reverse.py +++ b/djangorestframework/tests/reverse.py @@ -3,10 +3,10 @@ from django.core.urlresolvers import reverse from django.test import TestCase from django.utils import simplejson as json -from djangorestframework.views import BaseView +from djangorestframework.views import View -class MockView(BaseView): +class MockView(View): """Mock resource which simply returns a URL, so that we can ensure that reversed URLs are fully qualified""" permissions = () diff --git a/djangorestframework/tests/throttling.py b/djangorestframework/tests/throttling.py index e7a054cd0..a8f08b184 100644 --- a/djangorestframework/tests/throttling.py +++ b/djangorestframework/tests/throttling.py @@ -3,11 +3,11 @@ from django.test import TestCase from django.utils import simplejson as json from djangorestframework.compat import RequestFactory -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.permissions import PerUserThrottling -class MockView(BaseView): +class MockView(View): permissions = ( PerUserThrottling, ) throttle = (3, 1) # 3 requests per second diff --git a/djangorestframework/tests/validators.py b/djangorestframework/tests/validators.py index 32fb10029..a1e5d2d71 100644 --- a/djangorestframework/tests/validators.py +++ b/djangorestframework/tests/validators.py @@ -4,7 +4,7 @@ from django.test import TestCase from djangorestframework.compat import RequestFactory from djangorestframework.resources import Resource, FormResource, ModelResource from djangorestframework.response import ErrorResponse -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.resources import Resource @@ -18,7 +18,7 @@ class TestDisabledValidations(TestCase): class DisabledFormResource(FormResource): form = None - class MockView(BaseView): + class MockView(View): resource = DisabledFormResource view = MockView() @@ -31,7 +31,7 @@ class TestDisabledValidations(TestCase): class DisabledFormResource(FormResource): form = None - class MockView(BaseView): + class MockView(View): resource = DisabledFormResource view = MockView() @@ -43,7 +43,7 @@ class TestDisabledValidations(TestCase): """If the view's form is None and does not have a Resource with a model set then ModelFormValidator(view).validate_request(content, None) should just return the content unmodified.""" - class DisabledModelFormView(BaseView): + class DisabledModelFormView(View): resource = ModelResource view = DisabledModelFormView() @@ -52,7 +52,7 @@ class TestDisabledValidations(TestCase): def test_disabled_model_form_validator_get_bound_form_returns_none(self): """If the form attribute is None on FormValidatorMixin then get_bound_form(content) should just return None.""" - class DisabledModelFormView(BaseView): + class DisabledModelFormView(View): resource = ModelResource view = DisabledModelFormView() @@ -77,7 +77,7 @@ class TestNonFieldErrors(TestCase): class MockResource(FormResource): form = MockForm - class MockView(BaseView): + class MockView(View): pass view = MockView() @@ -104,10 +104,10 @@ class TestFormValidation(TestCase): class MockModelResource(ModelResource): form = MockForm - class MockFormView(BaseView): + class MockFormView(View): resource = MockFormResource - class MockModelFormView(BaseView): + class MockModelFormView(View): resource = MockModelResource self.MockFormResource = MockFormResource @@ -277,7 +277,7 @@ class TestModelFormValidator(TestCase): class MockResource(ModelResource): model = MockModel - class MockView(BaseView): + class MockView(View): resource = MockResource self.validator = MockResource(MockView) diff --git a/djangorestframework/views.py b/djangorestframework/views.py index 545671a4c..3d6a6c403 100644 --- a/djangorestframework/views.py +++ b/djangorestframework/views.py @@ -6,16 +6,17 @@ By setting or modifying class attributes on your view, you change it's predefine """ from django.core.urlresolvers import set_script_prefix +from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt -from djangorestframework.compat import View +from djangorestframework.compat import View as DjangoView from djangorestframework.response import Response, ErrorResponse from djangorestframework.mixins import * from djangorestframework import resources, renderers, parsers, authentication, permissions, status __all__ = ( - 'BaseView', + 'View', 'ModelView', 'InstanceModelView', 'ListModelView', @@ -24,7 +25,7 @@ __all__ = ( -class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): +class View(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, DjangoView): """ Handles incoming requests and maps them to REST operations. Performs request deserialization, response serialization, authentication and input validation. @@ -65,7 +66,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): as an attribute on the callable function. This allows us to discover information about the view when we do URL reverse lookups. """ - view = super(BaseView, cls).as_view(**initkwargs) + view = super(View, cls).as_view(**initkwargs) view.cls_instance = cls(**initkwargs) return view @@ -86,6 +87,14 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): {'detail': 'Method \'%s\' not allowed on this resource.' % self.method}) + def initial(self, request, *args, **kargs): + """ + Hook for any code that needs to run prior to anything else. + Required if you want to do things like set `request.upload_handlers` before + the authentication and dispatch handling is run. + """ + pass + # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. @csrf_exempt @@ -99,6 +108,8 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): set_script_prefix(prefix) try: + self.initial(request, *args, **kwargs) + # Authenticate and check request has the relevant permissions self._check_permissions() @@ -110,8 +121,10 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): response_obj = handler(request, *args, **kwargs) - # Allow return value to be either Response, or an object, or None - if isinstance(response_obj, Response): + # Allow return value to be either HttpResponse, Response, or an object, or None + if isinstance(response_obj, HttpResponse): + return response_obj + elif isinstance(response_obj, Response): response = response_obj elif response_obj is not None: response = Response(status.HTTP_200_OK, response_obj) @@ -135,7 +148,7 @@ class BaseView(ResourceMixin, RequestMixin, ResponseMixin, AuthMixin, View): -class ModelView(BaseView): +class ModelView(View): """A RESTful view that maps to a model in the database.""" resource = resources.ModelResource diff --git a/examples/objectstore/views.py b/examples/objectstore/views.py index 076e59416..19999aa95 100644 --- a/examples/objectstore/views.py +++ b/examples/objectstore/views.py @@ -1,7 +1,7 @@ from django.conf import settings from django.core.urlresolvers import reverse -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.response import Response from djangorestframework import status @@ -25,7 +25,7 @@ def remove_oldest_files(dir, max_files): [os.remove(path) for path in ctime_sorted_paths[max_files:]] -class ObjectStoreRoot(BaseView): +class ObjectStoreRoot(View): """ Root of the Object Store API. Allows the client to get a complete list of all the stored objects, or to create a new stored object. @@ -51,7 +51,7 @@ class ObjectStoreRoot(BaseView): return Response(status.HTTP_201_CREATED, self.CONTENT, {'Location': reverse('stored-object', kwargs={'key':key})}) -class StoredObject(BaseView): +class StoredObject(View): """ Represents a stored object. The object may be any picklable content. diff --git a/examples/pygments_api/views.py b/examples/pygments_api/views.py index e6bfae489..76647107f 100644 --- a/examples/pygments_api/views.py +++ b/examples/pygments_api/views.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse from djangorestframework.resources import FormResource from djangorestframework.response import Response from djangorestframework.renderers import BaseRenderer -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework import status from pygments.formatters import HtmlFormatter @@ -53,7 +53,7 @@ class PygmentsFormResource(FormResource): form = PygmentsForm -class PygmentsRoot(BaseView): +class PygmentsRoot(View): """ This example demonstrates a simple RESTful Web API aound the awesome pygments library. This top level resource is used to create highlighted code snippets, and to list all the existing code snippets. @@ -88,7 +88,7 @@ class PygmentsRoot(BaseView): return Response(status.HTTP_201_CREATED, headers={'Location': reverse('pygments-instance', args=[unique_id])}) -class PygmentsInstance(BaseView): +class PygmentsInstance(View): """ Simply return the stored highlighted HTML file with the correct mime type. This Resource only renders HTML and uses a standard HTML renderer rather than the renderers.DocumentingHTMLRenderer class. diff --git a/examples/resourceexample/views.py b/examples/resourceexample/views.py index 70d96891b..29651fbf5 100644 --- a/examples/resourceexample/views.py +++ b/examples/resourceexample/views.py @@ -1,6 +1,6 @@ from django.core.urlresolvers import reverse -from djangorestframework.views import BaseView +from djangorestframework.views import View from djangorestframework.resources import FormResource from djangorestframework.response import Response from djangorestframework import status @@ -14,7 +14,7 @@ class MyFormValidation(FormResource): form = MyForm -class ExampleResource(BaseView): +class ExampleResource(View): """ A basic read-only resource that points to 3 other resources. """ @@ -23,7 +23,7 @@ class ExampleResource(BaseView): return {"Some other resources": [reverse('another-example-resource', kwargs={'num':num}) for num in range(3)]} -class AnotherExampleResource(BaseView): +class AnotherExampleResource(View): """ A basic GET-able/POST-able resource. """ diff --git a/examples/sandbox/views.py b/examples/sandbox/views.py index d5b59284b..1c55c28f2 100644 --- a/examples/sandbox/views.py +++ b/examples/sandbox/views.py @@ -1,10 +1,10 @@ """The root view for the examples provided with Django REST framework""" from django.core.urlresolvers import reverse -from djangorestframework.views import BaseView +from djangorestframework.views import View -class Sandbox(BaseView): +class Sandbox(View): """This is the sandbox for the examples provided with [Django REST framework](http://django-rest-framework.org). These examples are provided to help you get a better idea of the some of the features of RESTful APIs created using the framework.