Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django.core.serializers.json import DjangoJSONEncoder
4from django.http import HttpResponse, HttpResponseBadRequest, Http404, HttpResponseNotAllowed
5import json
6from superdjango.storage.backends.ajax.local import LocalAjaxStorage
7from superdjango.storage.signals import ajax_file_uploaded
9# Exports
11__all__ = (
12 "AjaxFileUploader",
13)
15# Classes
18class AjaxFileUploader(object):
19 """This pseudo-view class provides support for uploading files via AJAX.
21 **Setup**
23 The JavaScript files for AJAX upload are located in the assets app, so make sure you have this in your
24 ``settings.py`` file:
26 .. code-block:: python
28 INSTALL_APPS = [
29 # ...
30 "superdjango.assets.apps.DefaultConfig",
31 # ...
32 ]
34 **Usage**
36 Create a view for handling the upload. In the example below, the `ajax_uploader` view is initialized (without any
37 options) at the bottom of the file. The ``upload_form()`` view is used to present an AJAX-enabled upload form. You
38 may wish to integrate this into your own form view and template instead.
40 .. code-block:: python
42 from django.middleware.csrf import get_token
43 from django.shortcuts import render_to_response
44 from django.template import RequestContext
45 from superdjango.ajax.uploads.views import AjaxFileUploader
47 def upload_form(request):
48 csrf_token = get_token(request)
49 return render_to_response(
50 'upload_form.html',
51 {'csrf_token': csrf_token},
52 context_instance=RequestContext(request)
53 )
55 ajax_uploader = AjaxFileUploader()
57 Add the views to your main ``urls.py`` file:
59 .. code-block:: python
61 from superdjango.ajax import views as ajax_upload_views
63 urlpatterns = [
64 # ...
65 path(r'ajax/upload/start/)', ajax_upload_views.upload_form, name="upload_form"),
66 path(r'ajax/upload/', ajax_upload_views.import_uploader, name="ajax_upload"),
67 # ...
68 ]
70 Create a template or add to the template where the upload should occur:
72 .. code-block:: html
74 {% load i18n %}
75 {% load static %}
76 <!doctype html>
77 <head>
78 <script src="{% static "bundled/fileuploader/js/fileuploader.js" %}" ></script>
79 <link href="{% static "bundled/fileuploader/css/fileuploader.css" %}" rel="stylesheet" />
80 <script>
81 $(function(){
82 var uploader = new qq.FileUploader({
83 action: "{% url ajax_upload %}",
84 element: $('#file-uploader')[0],
85 multiple: true,
86 onComplete: function(id, fileName, responseJSON) {
87 if (responseJSON.success) {
88 alert("success!");
89 }
90 else {
91 alert("upload failed!");
92 }
93 },
94 onAllComplete: function(uploads) {
95 // uploads is an array of maps
96 // the maps look like this: {file: FileObject, response: JSONServerResponse}
97 alert("All complete!");
98 },
99 params: {
100 'csrf_token': '{{ csrf_token }}',
101 'csrf_name': 'csrfmiddlewaretoken',
102 'csrf_xname': 'X-CSRFToken',
103 },
104 });
105 });
106 </script>
107 </head>
108 <body>
109 <div id="file-uploader">
110 <noscript>
111 <p>{% trans "Please enable JavaScript to use the file uploader." %}</p>
112 </noscript>
113 </div>
114 </body>
115 </html>
117 """
119 def __init__(self, backend=None, **kwargs):
120 """Initialize the view.
122 :param backend: The backend class to use. Defaults to :py:class:`LocalAjaxStorage`.
124 kwargs are passed to the backend upon instantiation.
126 """
127 if backend is None:
128 backend = LocalAjaxStorage
130 self.get_backend = lambda: backend(**kwargs)
132 def __call__(self, request, *args, **kwargs):
133 return self._ajax_upload(request, *args, **kwargs)
135 def _ajax_upload(self, request, *args, **kwargs):
136 if request.method == "POST":
137 if request.is_ajax():
138 # the file is stored raw in the request
139 upload = request
140 is_raw = True
141 # AJAX Upload will pass the filename in the querystring if it
142 # is the "advanced" ajax upload
143 try:
144 if 'qqfile' in request.GET:
145 filename = request.GET['qqfile']
146 else:
147 filename = request.REQUEST['qqfilename']
148 except KeyError:
149 return HttpResponseBadRequest("AJAX request not valid")
150 # not an ajax upload, so it was the "basic" iframe version with
151 # submission via form
152 else:
153 is_raw = False
154 if len(request.FILES) == 1:
155 # FILES is a dictionary in Django but Ajax Upload gives
156 # the uploaded file an ID based on a random number, so it
157 # cannot be guessed here in the code. Rather than editing
158 # Ajax Upload to pass the ID in the querystring, observe
159 # that each upload is a separate request, so FILES should
160 # only have one entry. Thus, we can just grab the first
161 # (and only) value in the dict.
162 upload = request.FILES.values()[0]
163 else:
164 raise Http404("Bad Upload")
166 filename = upload.name
168 backend = self.get_backend()
170 # custom filename handler
171 filename = (backend.update_filename(request, filename, *args, **kwargs)
172 or filename)
173 # save the file
174 backend.setup(filename, *args, **kwargs)
175 success = backend.upload(upload, filename, is_raw, *args, **kwargs)
177 if success:
178 ajax_file_uploaded.send(sender=self.__class__, backend=backend, request=request)
180 # callback
181 extra_context = backend.upload_complete(request, filename, *args, **kwargs)
183 # let Ajax Upload know whether we saved it or not
184 ret_json = {'success': success, 'filename': filename}
185 if extra_context is not None:
186 ret_json.update(extra_context)
188 # although "application/json" is the correct content type, IE throws a fit
189 return HttpResponse(json.dumps(ret_json, cls=DjangoJSONEncoder), content_type='text/html; charset=utf-8')
190 else:
191 response = HttpResponseNotAllowed(['POST'])
192 response.write("ERROR: Only POST allowed")
193 return response