How do I modify the file upload handlers in a class based View with CSRF middleware?
Question: In my Django project I will have to modify the tuple of file upload handlers
"on the fly" [as
documented](https://docs.djangoproject.com/en/dev/topics/http/file-
uploads/#modifying-upload-handlers-on-the-fly), to have the ability to modify
the file stream as it is being uploaded. I need this "on the fly", because I
have to provide the handler some data from the View (see `setup()` method in
the code below).

The documentation also mentions how to take care of doing this if you use CSRF
protection. This is special because the CSRF protection middleware accesses
the POST data in the request resulting in the file upload process will be
fired at the time before my View gets called. However, this is only documented
for old-style Views, but I want to accomplish the same using a Class Based
View.

Here's a minimal code example of my View:

    
    
    from django.views.decorators.csrf import csrf_exempt, csrf_protect
    
    class MyView(TemplateResponseMixin, ContextMixin, View):
        template_name = 'mytemplate.html'
    
        def __init__(self, *args, **kwargs):
            self.fileuploadhandler = MyUploadHandler()
            super(MyView, self).__init__(*args, **kwargs)
    
        def get(self, request, *args, **kwargs):
            return self.render_to_response(
                self.get_context_data(form=MyForm()))
    
        #@csrf_protect                               # this gives the error below
        def post(self, request, *args, **kwargs):
            # Set up the FileUploadHandler
            # SNIP - some data is being gathered here
            self.fileuploadhandler.setup(mydata)
    
            # Process the POST data by loading the ModelForm
            form = MyForm(request.POST, request.FILES)
            if form.is_valid():
                # SNIP processing Form
            else:
                return self.render_to_response(self.get_context_data(form=form))
    
        def get_context_data(self, **kwargs):
            context = super(MyView, self).get_context_data(**kwargs)
            return context
    
        @csrf_exempt                                 # I have to do this
        def dispatch(self, *args, **kwargs):
            self.request.upload_handlers.insert(0, self.fileuploadhandler)
            return super(MyView, self).dispatch(*args, **kwargs)
    

The error I get when using `@csrf_protect` on the `post` method is:

    
    
    Traceback (most recent call last):
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
        response = callback(request, *callback_args, **callback_kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
        return self.dispatch(request, *args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 25, in _wrapper
        return bound_func(*args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py", line 25, in _wrapped_view
        return view_func(request, *args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 21, in bound_func
        return func(self, *args2, **kwargs2)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 25, in _wrapper
        return bound_func(*args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/contrib/auth/decorators.py", line 25, in _wrapped_view
        return view_func(request, *args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 21, in bound_func
        return func(self, *args2, **kwargs2)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
        return view_func(*args, **kwargs)
      File "/some/path/to/project/myapp/views.py", line 01234, in dispatch
        return super(MyView, self).dispatch(*args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 86, in dispatch
        return handler(request, *args, **kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/utils/decorators.py", line 87, in _wrapped_view
        result = middleware.process_view(request, view_func, args, kwargs)
      File "/some/path/to/Envs/someenv/local/lib/python2.7/site-packages/django/middleware/csrf.py", line 95, in process_view
        request.COOKIES[settings.CSRF_COOKIE_NAME])
    AttributeError: 'MyView' object has no attribute 'COOKIES'
    

So, how can I have the combination of the following three properties of my
View?

  * the use of Class Based Views
  * ability to modify the file upload handler "on the fly"
  * proper CSRF protection on the View

Django version used: 1.5.1, Python 2.7.3.


Answer: With the help of a colleague I've found a bit of an ugly way of using the CSRF
middleware to check the token manually within the View. Here's the recipe:

    
    
    from django.views.decorators.csrf import csrf_exempt, csrf_protect
    from django.middleware.csrf import CsrfViewMiddleware
    
    class MyView(TemplateResponseMixin, ContextMixin, View):
        template_name = 'mytemplate.html'
    
        def __init__(self, *args, **kwargs):
            self.fileuploadhandler = MyUploadHandler()
            super(MyView, self).__init__(*args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            # Set up the FileUploadHandler
            # SNIP - some data is being gathered here
            self.fileuploadhandler.setup(mydata)
    
            # Check CSRF manually *after* initializing the file upload handlers.
            csrf_checker = CsrfViewMiddleware()
            csrf_error = csrf_checker.process_view(request, None, None, None)
            if csrf_error is not None:
                return csrf_error # csrf_error is the regular CSRF error View
    
            # Process the POST data by loading the ModelForm
            form = MyForm(request.POST, request.FILES)
            if form.is_valid():
                # SNIP processing Form
            else:
                return self.render_to_response(self.get_context_data(form=form))
    
        @csrf_exempt # Important to skip CSRF checking here.
        def dispatch(self, *args, **kwargs):
            self.request.upload_handlers.insert(0, self.fileuploadhandler)
            return super(MyView, self).dispatch(*args, **kwargs)
    

I think here's some room for improvement in Django - the CSRF middleware
should provide a separate `check_token` method wrapped in `process_view`, in
my opinion.

