在我的Django项目中,我必须"动态"修改文件上传处理程序的元组,以便能够在上传时修改文件流。我需要这个"动态",因为我必须从View向处理程序提供一些数据(请参阅下面代码中的setup()
方法)。
该文档还提到了如果您使用CSRF保护,如何做到这一点。这很特别,因为CSRF保护中间件访问请求中的POST数据,导致文件上传过程将在调用我的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)
在post
方法上使用@csrf_protect
时,我得到的错误是:
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'
那么,如何将我的视图的以下三个属性组合起来呢?
- 基于类的视图的使用
- "动态"修改文件上传处理程序的能力
- 视图上的正确CSRF保护
使用的Django版本:1.5.1,Python 2.7.3。
在一位同事的帮助下,我发现了一种使用CSRF中间件在View中手动检查令牌的方法。配方如下:
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)
我认为Django还有一些改进的空间——在我看来,CSRF中间件应该提供一个单独的check_token
方法,封装在process_view
中。