Dear all,
In order to provide progress feedback of file uploads I needed to
install an upload handler for a specific view. This is documented for
"classical" views at
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#modifying-upload-handlers-on-the-fly
For generic views, however, I could not find any instructions and I
came up with the following, which I would like to share:
# BEGIN OF CLASS
from django.utils import importlib
from django.core.exceptions import ImproperlyConfigured
from django.views.decorators.csrf import csrf_protect
class UploadHandlerMixin(object):
'''
A mixin for Django generic views that installs a custom upload
handler in front of
the current chain of upload handlers.
You specify the handler to install by overriding the
'upload_handler' attribute of
the class, specifying the module and class name in the form
'path.to.module.class':
class MyView(UploadHandlerMixin, View):
upload_handler = 'path.to.module.MyUploadHandler'
If you do not override 'upload_handler', no additional upload
handler will be
installed.
If the CsrfViewMiddleware is installed (which is the default)
then you must use
your view as follows in your urls.py:
from django.views.decorators.csrf import csrf_exempt
url(r'^.../$', csrf_exempt(MyView.as_view()), ...),
Internally, the UploadHandlerMixin mixin will install the
upload handler and then
perform the CSRF check. (This is necessary because the CSRF
check inspects
request.POST, and afterwards upload handlers cannot be
changed, see documentation
link given below.)
The handler is installed as described in the Django
documentation "Modifying upload handlers
on the fly", see
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#modifying-upload-handlers-on-the-fly
'''
upload_handler = None
def dispatch(self, request, *args, **kwargs):
if not self.upload_handler is None:
request.upload_handlers.insert(0,
UploadHandlerMixin._instantiate_upload_handler(self.upload_handler,
request))
return _uploadhandler_dispatch(request, self, *args, **kwargs)
@staticmethod
def _instantiate_upload_handler(path, *args, **kwargs):
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
mod = importlib.import_module(module)
except ImportError, e:
raise ImproperlyConfigured('Error importing upload handler
module %s: "%s"' % (module, e))
except ValueError, e:
raise ImproperlyConfigured('Error importing upload handler
module. Is FILE_UPLOAD_HANDLERS a correctly defined list or tuple?')
try:
cls = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured('Module "%s" does not define a
"%s" upload handler backend' % (module, attr))
return cls(*args, **kwargs)
@csrf_protect
def _uploadhandler_dispatch(request, view, *args, **kwargs):
return super(UploadHandlerMixin, view).dispatch(request, *args,
**kwargs)
# END OF CLASS
Not being a Django expert, I wanted to ask whether this is okay
security-wise? Any other feedback is of course welcome.
Best,
Kaspar
P.S. I opened a question at stackoverflow,
http://stackoverflow.com/questions/7901895/how-to-install-a-custom-upload-handler-in-a-django-generic-view
and will keep both this thread and the latter post updated.