tutoriales - vistas basadas en clases django
¿Cómo modifico los manejadores de carga de archivos en una vista basada en clases con middleware CSRF? (1)
Con la ayuda de un colega, he encontrado una manera fea de usar el middleware CSRF para verificar el token manualmente dentro de View. Aquí está la receta:
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)
Creo que hay margen de mejora en Django: en mi opinión, el middleware de CSRF debería proporcionar un método check_token
separado check_token
en process_view
.
En mi proyecto de Django, tendré que modificar la tupla de los manejadores de carga de archivos "sobre la marcha" según lo documentado , para poder modificar la secuencia de archivos a medida que se cargan. Necesito esto "sobre la marcha", porque tengo que proporcionarle al manejador algunos datos de la Vista (vea setup()
método setup()
en el código a continuación).
La documentación también menciona cómo cuidar de hacer esto si usa protección CSRF. Esto es especial porque el middleware de protección CSRF accede a los datos POST en la solicitud, lo que da como resultado que el proceso de carga de archivos se active en el momento anterior a que se llame a mi vista. Sin embargo, esto solo está documentado para Vistas antiguas, pero quiero lograr lo mismo con una Vista basada en clase.
Aquí hay un ejemplo de código mínimo de mi Vista:
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)
El error que obtengo al usar @csrf_protect
en el método de post
es:
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''
Entonces, ¿cómo puedo tener la combinación de las siguientes tres propiedades de mi Vista?
- el uso de vistas basadas en clase
- capacidad de modificar el controlador de carga de archivos "sobre la marcha"
- adecuada protección CSRF en la vista
Versión de Django utilizada: 1.5.1, Python 2.7.3.