From 8fb62852e0289d826f750a72c32e909844a14d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Wed, 18 Sep 2013 15:09:26 -0300 Subject: [PATCH 1/8] Separate serialiazation and json response from view processing. --- fileupload/response.py | 27 +++++++++++++++ fileupload/serialize.py | 36 +++++++++++++++++++ fileupload/views.py | 77 ++++++++++++----------------------------- 3 files changed, 86 insertions(+), 54 deletions(-) create mode 100644 fileupload/response.py create mode 100644 fileupload/serialize.py diff --git a/fileupload/response.py b/fileupload/response.py new file mode 100644 index 0000000..26324be --- /dev/null +++ b/fileupload/response.py @@ -0,0 +1,27 @@ +# encoding: utf-8 +from django.http import HttpResponse +from django.utils import simplejson + +MIMEANY = '*/*' +MIMEJSON = 'application/json' +MIMETEXT = 'text/plain' + + +def response_mimetype(request): + """response_mimetype -- Return a proper response mimetype, accordingly to + what the client accepts, as available in the `HTTP_ACCEPT` header. + + request -- a HttpRequest instance. + + """ + can_json = MIMEJSON in request.META['HTTP_ACCEPT'] + can_json |= MIMEANY in request.META['HTTP_ACCEPT'] + return MIMEJSON if can_json else MIMETEXT + + +class JSONResponse(HttpResponse): + """JSON response class.""" + def __init__(self, obj='', json_opts=None, mimetype=MIMEJSON, *args, **kwargs): + json_opts = json_opts if isinstance(json_opts, dict) else {} + content = simplejson.dumps(obj, **json_opts) + super(JSONResponse, self).__init__(content, mimetype, *args, **kwargs) diff --git a/fileupload/serialize.py b/fileupload/serialize.py new file mode 100644 index 0000000..f746c83 --- /dev/null +++ b/fileupload/serialize.py @@ -0,0 +1,36 @@ +# encoding: utf-8 +import mimetypes +import re +from django.core.urlresolvers import reverse + + +def order_name(name): + """order_name -- Limit the name to 20 chars length, and convert to a + ellipsed string. + + name -- text to be limited. + + """ + name = re.sub (r'^.*/', '', name) + if len(name)>20: + return name[:10] + "..." + name[-7:] + else: + return name + + +def serialize(instance): + """serialize -- Serialize a Picture instance into a `json` object. + + instance -- Picture instance + """ + return { + 'url': instance.file.url, + 'name': order_name(instance.file.name), + 'type': mimetypes.guess_type(instance.file.path)[0] or 'image/png', + 'thumbnailUrl': instance.file.url, + 'size': instance.file.size, + 'deleteUrl': reverse('upload-delete', args=[instance.pk]), + 'deleteType': 'DELETE', + } + + diff --git a/fileupload/views.py b/fileupload/views.py index 18a9dc8..20e09d2 100644 --- a/fileupload/views.py +++ b/fileupload/views.py @@ -1,86 +1,55 @@ -from fileupload.models import Picture -from django.views.generic import CreateView, DeleteView +# encoding: utf-8 +from django.views.generic import CreateView, DeleteView, View +from .models import Picture +from .response import JSONResponse, response_mimetype +from .serialize import serialize -from django.http import HttpResponse, HttpResponseRedirect -from django.utils import simplejson -from django.core.urlresolvers import reverse - -from django.conf import settings -import re - -def response_mimetype(request): - if "application/json" in request.META['HTTP_ACCEPT']: - return "application/json" - else: - return "text/plain" - -def orderName(name): - name = re.sub (r'^.*/', '', name) - if len(name)>20: - return name[:10] + "..." + name[-7:] - else: - return name class PictureCreateView(CreateView): model = Picture def form_valid(self, form): self.object = form.save() - f = self.request.FILES.get('file') - files = [{ - 'url': self.object.file.url, - 'name': orderName(f.name), - "type": "image/png", - 'thumbnailUrl': self.object.file.url, - 'size': f.size, - 'deleteUrl': reverse('upload-delete', args=[self.object.id]), - 'deleteType': "DELETE", - }] + files = [serialize(self.object)] data = {"files": files} - response = JSONResponse(data, {}, response_mimetype(self.request)) + response = JSONResponse(data, mimetype=response_mimetype(self.request)) response['Content-Disposition'] = 'inline; filename=files.json' return response + class BasicVersionCreateView(PictureCreateView): template_name_suffix = '_basic_form' + class BasicPlusVersionCreateView(PictureCreateView): template_name_suffix = '_basicplus_form' + class AngularVersionCreateView(PictureCreateView): template_name_suffix = '_angular_form' + class jQueryVersionCreateView(PictureCreateView): template_name_suffix = '_jquery_form' + class PictureDeleteView(DeleteView): model = Picture def delete(self, request, *args, **kwargs): self.object = self.get_object() self.object.delete() - response = JSONResponse(True, {}, response_mimetype(self.request)) + response = JSONResponse(True, mimetype=response_mimetype(request)) response['Content-Disposition'] = 'inline; filename=files.json' return response -def PictureListView(request): - files = [] - for obj in Picture.objects.all(): - files += [{ - 'name': orderName(obj.file.name), - 'size': obj.file.size, - 'url': obj.file.url, - 'thumbnailUrl': obj.file.url, - 'deleteUrl': reverse('upload-delete', args=[obj.id]), - 'deleteType': "DELETE" - }] - data = {"files": files} - response = JSONResponse(data, {}, response_mimetype(request)) - response['Content-Disposition'] = 'inline; filename=files.json' - return response - -class JSONResponse(HttpResponse): - """JSON response class.""" - def __init__(self,obj='',json_opts={},mimetype="application/json",*args,**kwargs): - content = simplejson.dumps(obj,**json_opts) - super(JSONResponse,self).__init__(content,mimetype,*args,**kwargs) + +class PictureListView(View): + def get(self, request, *args, **kwargs): + files = [] + for obj in Picture.objects.all(): + files.append(serialize(obj)) + data = {'files': files} + response = JSONResponse(data, mimetype=response_mimetype(request)) + response['Content-Disposition'] = 'inline; filename=files.json' + return response From cf8c3f139c04295939032e85eae37ade46ca4611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Wed, 18 Sep 2013 15:10:36 -0300 Subject: [PATCH 2/8] Improve urls formatting. --- fileupload/urls.py | 49 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/fileupload/urls.py b/fileupload/urls.py index bfdf903..4e43070 100644 --- a/fileupload/urls.py +++ b/fileupload/urls.py @@ -1,15 +1,42 @@ from django.conf.urls import patterns, url -from fileupload.views import BasicVersionCreateView, BasicPlusVersionCreateView, PictureCreateView, AngularVersionCreateView, jQueryVersionCreateView, PictureDeleteView +from .views import (BasicVersionCreateView, BasicPlusVersionCreateView, + PictureCreateView, AngularVersionCreateView, jQueryVersionCreateView, + PictureDeleteView, PictureListView) urlpatterns = patterns('', - (r'^basic/$', BasicVersionCreateView.as_view(), {}, 'upload-basic'), - (r'^basic/plus/$', BasicPlusVersionCreateView.as_view(), {}, 'upload-basic-plus'), - (r'^new/$', PictureCreateView.as_view(), {}, 'upload-new'), - (r'^angular/$', AngularVersionCreateView.as_view(), {}, 'upload-angular'), - (r'^jquery-ui/$', jQueryVersionCreateView.as_view(), {}, 'upload-jquery'), - (r'^delete/(?P\d+)$', PictureDeleteView.as_view(), {}, 'upload-delete'), - url(r'^view/$', 'fileupload.views.PictureListView', name='upload-view'), + url( + r'^basic/$', + BasicVersionCreateView.as_view(), + name='upload-basic' + ), + url( + r'^basic/plus/$', + BasicPlusVersionCreateView.as_view(), + name='upload-basic-plus' + ), + url( + r'^new/$', + PictureCreateView.as_view(), + name='upload-new' + ), + url( + r'^angular/$', + AngularVersionCreateView.as_view(), + name='upload-angular' + ), + url( + r'^jquery-ui/$', + jQueryVersionCreateView.as_view(), + name='upload-jquery' + ), + url( + r'^delete/(?P\d+)$', + PictureDeleteView.as_view(), + name='upload-delete' + ), + url( + r'^view/$', + PictureListView.as_view(), + name='upload-view' + ), ) - - - From b3ece12190a414f62cb913f6f13468e35d795a6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Wed, 18 Sep 2013 15:18:11 -0300 Subject: [PATCH 3/8] Apply PEP8 formatting. --- fileupload/models.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/fileupload/models.py b/fileupload/models.py index 63a3b0e..f28d242 100644 --- a/fileupload/models.py +++ b/fileupload/models.py @@ -1,13 +1,14 @@ +# encoding: utf-8 from django.db import models -class Picture(models.Model): - # This is a small demo using just two fields. The slug field is really not - # necessary, but makes the code simpler. ImageField depends on PIL or - # pillow (where Pillow is easily installable in a virtualenv. If you have - # problems installing pillow, use a more generic FileField instead. +class Picture(models.Model): + """This is a small demo using just two fields. The slug field is really not + necessary, but makes the code simpler. ImageField depends on PIL or + pillow (where Pillow is easily installable in a virtualenv. If you have + problems installing pillow, use a more generic FileField instead. - #file = models.FileField(upload_to="pictures") + """ file = models.ImageField(upload_to="pictures") slug = models.SlugField(max_length=50, blank=True) @@ -22,7 +23,7 @@ def save(self, *args, **kwargs): self.slug = self.file.name super(Picture, self).save(*args, **kwargs) - # remove to leave file. def delete(self, *args, **kwargs): + """delete -- Remove to leave file.""" self.file.delete(False) super(Picture, self).delete(*args, **kwargs) From ef55de7907fa84ccc9da7bee7aae650a8c82eecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Thu, 19 Sep 2013 08:11:26 -0300 Subject: [PATCH 4/8] Remove extra space for method call. --- fileupload/serialize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fileupload/serialize.py b/fileupload/serialize.py index f746c83..3244f8e 100644 --- a/fileupload/serialize.py +++ b/fileupload/serialize.py @@ -11,7 +11,7 @@ def order_name(name): name -- text to be limited. """ - name = re.sub (r'^.*/', '', name) + name = re.sub(r'^.*/', '', name) if len(name)>20: return name[:10] + "..." + name[-7:] else: From b612edf855a53c6924232c0adbc30a1a5ff28094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Mon, 23 Sep 2013 17:16:53 -0300 Subject: [PATCH 5/8] Improve response documentation. --- fileupload/response.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/fileupload/response.py b/fileupload/response.py index 26324be..829685a 100644 --- a/fileupload/response.py +++ b/fileupload/response.py @@ -20,7 +20,18 @@ def response_mimetype(request): class JSONResponse(HttpResponse): - """JSON response class.""" + """JSONResponse -- Extends HTTPResponse to handle JSON format response. + + This response can be used in any view that should return a json stream of + data. + + Usage: + + def a_iew(request): + content = {'key': 'value'} + return JSONResponse(content, mimetype=response_mimetype(request)) + + """ def __init__(self, obj='', json_opts=None, mimetype=MIMEJSON, *args, **kwargs): json_opts = json_opts if isinstance(json_opts, dict) else {} content = simplejson.dumps(obj, **json_opts) From eaeaa6fb9bc328c9ee1012924ff0ac59d7590564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Mon, 23 Sep 2013 17:17:22 -0300 Subject: [PATCH 6/8] Minor improvements in order_name and serialize methods. --- fileupload/serialize.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/fileupload/serialize.py b/fileupload/serialize.py index 3244f8e..4494e79 100644 --- a/fileupload/serialize.py +++ b/fileupload/serialize.py @@ -5,30 +5,32 @@ def order_name(name): - """order_name -- Limit the name to 20 chars length, and convert to a - ellipsed string. + """order_name -- Limit a text to 20 chars length, if necessary strips the + middle of the text and substitute it for an ellipsis. name -- text to be limited. """ name = re.sub(r'^.*/', '', name) - if len(name)>20: - return name[:10] + "..." + name[-7:] - else: + if len(name) <= 20: return name + return name[:10] + "..." + name[-7:] -def serialize(instance): - """serialize -- Serialize a Picture instance into a `json` object. +def serialize(instance, file_attr='file'): + """serialize -- Serialize a Picture instance into a dict. instance -- Picture instance + file_attr -- attribute name that contains the FileField or ImageField + """ + obj = getattr(instance, file_attr) return { - 'url': instance.file.url, - 'name': order_name(instance.file.name), - 'type': mimetypes.guess_type(instance.file.path)[0] or 'image/png', - 'thumbnailUrl': instance.file.url, - 'size': instance.file.size, + 'url': obj.url, + 'name': order_name(obj.name), + 'type': mimetypes.guess_type(obj.path)[0] or 'image/png', + 'thumbnailUrl': obj.url, + 'size': obj.size, 'deleteUrl': reverse('upload-delete', args=[instance.pk]), 'deleteType': 'DELETE', } From 650c6635e58988242e13fd20997af09c83a20954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo=20Dubas?= Date: Mon, 23 Sep 2013 17:17:42 -0300 Subject: [PATCH 7/8] Minor aesthetics changes. --- fileupload/urls.py | 1 + fileupload/views.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fileupload/urls.py b/fileupload/urls.py index 4e43070..d6d6f7e 100644 --- a/fileupload/urls.py +++ b/fileupload/urls.py @@ -1,3 +1,4 @@ +# encoding: utf-8 from django.conf.urls import patterns, url from .views import (BasicVersionCreateView, BasicPlusVersionCreateView, PictureCreateView, AngularVersionCreateView, jQueryVersionCreateView, diff --git a/fileupload/views.py b/fileupload/views.py index 20e09d2..2b06e79 100644 --- a/fileupload/views.py +++ b/fileupload/views.py @@ -11,7 +11,7 @@ class PictureCreateView(CreateView): def form_valid(self, form): self.object = form.save() files = [serialize(self.object)] - data = {"files": files} + data = {'files': files} response = JSONResponse(data, mimetype=response_mimetype(self.request)) response['Content-Disposition'] = 'inline; filename=files.json' return response From 206991a376c0d7ee9d431c82323b1da72593708d Mon Sep 17 00:00:00 2001 From: ET-CS Date: Sat, 28 Sep 2013 15:55:50 +0300 Subject: [PATCH 8/8] Revert "Improve urls formatting." This reverts commit cf8c3f139c04295939032e85eae37ade46ca4611. --- fileupload/urls.py | 49 +++++++++++----------------------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/fileupload/urls.py b/fileupload/urls.py index d6d6f7e..06de8ec 100644 --- a/fileupload/urls.py +++ b/fileupload/urls.py @@ -1,43 +1,16 @@ # encoding: utf-8 from django.conf.urls import patterns, url -from .views import (BasicVersionCreateView, BasicPlusVersionCreateView, - PictureCreateView, AngularVersionCreateView, jQueryVersionCreateView, - PictureDeleteView, PictureListView) +from fileupload.views import BasicVersionCreateView, BasicPlusVersionCreateView, PictureCreateView, AngularVersionCreateView, jQueryVersionCreateView, PictureDeleteView urlpatterns = patterns('', - url( - r'^basic/$', - BasicVersionCreateView.as_view(), - name='upload-basic' - ), - url( - r'^basic/plus/$', - BasicPlusVersionCreateView.as_view(), - name='upload-basic-plus' - ), - url( - r'^new/$', - PictureCreateView.as_view(), - name='upload-new' - ), - url( - r'^angular/$', - AngularVersionCreateView.as_view(), - name='upload-angular' - ), - url( - r'^jquery-ui/$', - jQueryVersionCreateView.as_view(), - name='upload-jquery' - ), - url( - r'^delete/(?P\d+)$', - PictureDeleteView.as_view(), - name='upload-delete' - ), - url( - r'^view/$', - PictureListView.as_view(), - name='upload-view' - ), + (r'^basic/$', BasicVersionCreateView.as_view(), {}, 'upload-basic'), + (r'^basic/plus/$', BasicPlusVersionCreateView.as_view(), {}, 'upload-basic-plus'), + (r'^new/$', PictureCreateView.as_view(), {}, 'upload-new'), + (r'^angular/$', AngularVersionCreateView.as_view(), {}, 'upload-angular'), + (r'^jquery-ui/$', jQueryVersionCreateView.as_view(), {}, 'upload-jquery'), + (r'^delete/(?P\d+)$', PictureDeleteView.as_view(), {}, 'upload-delete'), + url(r'^view/$', 'fileupload.views.PictureListView', name='upload-view'), ) + + +