diff --git a/.gitignore b/.gitignore index e74c193..8fef53c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,9 @@ db /share /env /venv +/django-jquery-file-upload/media *.log *.pot *.pyc -local_settings.py \ No newline at end of file +local_settings.py +.idea diff --git a/README.md b/README.md index 8f185a9..3498b31 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,17 @@ fileupload/models.py as commented in the file. Installation ============ -* pip install -r requirements.txt (will install django and pillow) -* python manage.py syncdb -* python manage.py runserver -* go to localhost:8000/upload/new/ and upload some files +I recommend to install this within a virtualenv. + +```sh +virtualenv -p python3 venv +source venv/bin/activate +pip install -r requirements.txt +./manage.py migrate +./manage.py runserver +``` + +And then go to localhost:8000 and try to upload some files. License ======= diff --git a/__init__.py b/django-jquery-file-upload/__init__.py similarity index 100% rename from __init__.py rename to django-jquery-file-upload/__init__.py diff --git a/django-jquery-file-upload/settings.py b/django-jquery-file-upload/settings.py new file mode 100644 index 0000000..d27d9db --- /dev/null +++ b/django-jquery-file-upload/settings.py @@ -0,0 +1,95 @@ +""" +Django settings. +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '9%$in^gpdaig@v3or_to&_z(=n)3)$f1mr3hf9e#kespy2ajlo' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = ( + 'fileupload.apps.FileuploadConfig', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'django-jquery-file-upload.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + #'DIRS': ['django-jquery-file-upload/templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'django-jquery-file-upload.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db'), + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'Europe/Oslo' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +STATIC_URL = '/static/' + +STATICFILES_DIRS = [ + 'django-jquery-file-upload/static', +] + +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'django-jquery-file-upload', 'media') diff --git a/django-jquery-file-upload/urls.py b/django-jquery-file-upload/urls.py new file mode 100644 index 0000000..8f26ec4 --- /dev/null +++ b/django-jquery-file-upload/urls.py @@ -0,0 +1,17 @@ +from django.urls import include, path +from django.http import HttpResponseRedirect +from django.conf import settings + +# Uncomment the next two lines to enable the admin: +from django.contrib import admin +admin.autodiscover() + +urlpatterns = [ + path('', lambda x: HttpResponseRedirect('/upload/new/')), + path('upload/', include('fileupload.urls')), + path('admin/', admin.site.urls), +] + +if settings.DEBUG: + from django.conf.urls.static import static + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/django-jquery-file-upload/wsgi.py b/django-jquery-file-upload/wsgi.py new file mode 100644 index 0000000..8e47fba --- /dev/null +++ b/django-jquery-file-upload/wsgi.py @@ -0,0 +1,11 @@ +""" +WSGI config for django-jquery-file-upload project. +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django-jquery-file-upload.settings") + +application = get_wsgi_application() diff --git a/fileupload/apps.py b/fileupload/apps.py new file mode 100644 index 0000000..ff22d3e --- /dev/null +++ b/fileupload/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FileuploadConfig(AppConfig): + name = 'fileupload' + diff --git a/fileupload/migrations/0001_initial.py b/fileupload/migrations/0001_initial.py new file mode 100644 index 0000000..0c157ef --- /dev/null +++ b/fileupload/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.6 on 2016-06-07 19:15 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Picture', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', models.ImageField(upload_to='pictures')), + ('slug', models.SlugField(blank=True)), + ], + ), + ] diff --git a/fileupload/migrations/__init__.py b/fileupload/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fileupload/models.py b/fileupload/models.py index 63a3b0e..81a20a3 100644 --- a/fileupload/models.py +++ b/fileupload/models.py @@ -1,17 +1,18 @@ +# 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) - def __unicode__(self): + def __str__(self): return self.file.name @models.permalink @@ -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) diff --git a/fileupload/response.py b/fileupload/response.py new file mode 100644 index 0000000..334eb29 --- /dev/null +++ b/fileupload/response.py @@ -0,0 +1,38 @@ +# encoding: utf-8 +from django.http import HttpResponse +import json + +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): + """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 = json.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..96749af --- /dev/null +++ b/fileupload/serialize.py @@ -0,0 +1,38 @@ +# encoding: utf-8 +import mimetypes +import re +from django.urls import reverse + + +def order_name(name): + """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 + return name[:10] + "..." + name[-7:] + + +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': 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', + } + + diff --git a/fileupload/static/js/jquery.fileupload.js b/fileupload/static/js/jquery.fileupload.js index d048a94..3a1ec70 100644 --- a/fileupload/static/js/jquery.fileupload.js +++ b/fileupload/static/js/jquery.fileupload.js @@ -805,6 +805,9 @@ response.jqXHR = options.jqXHR = jqXHR; response.textStatus = options.textStatus = textStatus; response.errorThrown = options.errorThrown = errorThrown; + if ('file' in response.jqXHR.responseJSON) { + response.errorThrown = options.errorThrown = response.jqXHR.responseJSON.file.join(", "); + } this._trigger('fail', null, options); }, diff --git a/fileupload/templates/fileupload/picture_angular_form.html b/fileupload/templates/fileupload/picture_angular_form.html index a5ac9c0..a6add16 100644 --- a/fileupload/templates/fileupload/picture_angular_form.html +++ b/fileupload/templates/fileupload/picture_angular_form.html @@ -24,13 +24,13 @@ - + - + - + - + - + - + - +