How do I modify the file upload handlers in a class based View with CSRF middleware?

孤人 提交于 2020-01-03 19:33:49

问题


In my Django project I will have to modify the tuple of file upload handlers "on the fly" as documented, 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.


回答1:


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.



来源:https://stackoverflow.com/questions/17507800/how-do-i-modify-the-file-upload-handlers-in-a-class-based-view-with-csrf-middlew

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!