Package: django-upload-form
django-upload-formA minimal (yet careful about UX) solution to upload multiple files in a Django project,
using a Django-like Form Class.PurposeThe purpose of this module is that of providing a simple yet effective tool
for managing the user interaction for files uploading in a Django projects,
with the following features:before submission:files can be added to the upload list either via drag&drop or with an file-open dialoguser can revise the upload list by adding or removing filesthe list displays informations about the files (name, tipe, size)during submission:image files are optionally resized up to a configurabile max resolutiona progress bar is shown during files uploadafter submission:a target view receives the uploaded files via an Ajax POST requesthere, you will validate the form …… and use the receivedfilesat your willAll this is obtained by leveraging HTML5 specific capabilities, and applying some clever (in my opinion)
and sometimes hacky techniques I learned from a few selected articles, listed in theReferencessection below.No external dependencies are required (other than jQuery).The codebase is deliberately minimal.How to usedjango-upload-formThe only purpose of the package is that of:managing user interactionupload the files upon form submissionWhat you do with the uploaded files is entirely up to you, and you’re responsible for this.These are the steps required to usedjango-upload-form:derive your own Form class from UploadForm, and override a few methodsprovide a view which will be the target for files uploadThe UploadForm-derived classAt the very minimum, you need to override the following methods:form_valid(self, request)Here is where you receive the list of (in-memory) uploaded files after submission;
What you do with the uploaded files is entirely up to you.
This method should normally returnget_success_url().get_success_url(self)Determine the URL to redirect to when the form is successfully validated.get_action(self, request=None)Returns the url of thetarget view(see below).Optionally, you can customize the following instance properties:self.allowed_file_typesList of allowed file types (ex: ‘.xls .xlsx .pdf’);
Default to: None, which means we’ll use the corresponding settingUPLOAD_FORM_ALLOWED_FILE_TYPESself.acceptReturns the value for the “accept” attribute of the file input element;
Defaults to: None, which means we’ll build it fromself.allowed_file_typeslist;
Example:image/*- which also trigger the lookup into the image gallery on mobile devicesself.max_image_sizeWhen > 0, images will be resized accordingly before uploading.
The required size is expressed in pixels.
Default: 0or override the corresponding methods:def get_accept(self, request=None)def get_max_image_size(self, request=None)Note: while “accept” is applied to the file input element, “allowed_file_types” is
used during form validation; it is your responsability to supply coherent values.When “accept” is None, on the other side, this is guaranteed by UploadForm.The target viewThe target view will receive the uploaded files with a POST request, and normally should:build a bounded formvalidate itinvoke form_valid()You will probably use the same view for form rendering (GET); this is quite common
practice, but entirely optional.Also note that, since upon form submission the POST request will be issued via Ajax,
the target view should cooperate by returning a JsonResponse().In practice, in the target viewyou should always follow the pattern below,
then cope with your specific needs inMyUploadForm.form_valid():defsample_view(request):ifrequest.method=='GET':form=TestUploadForm()else:form=TestUploadForm(request.POST,request.FILES)ifform.is_valid():url=form.form_valid(request)returnJsonResponse({'action':'redirect','url':url,})else:returnJsonResponse({'action':'replace','html':form.as_html(request),})returnrender(request,'upload_form/test_view.html',{'form':form,'form_as_html':form.as_html(request),})or (when the initial rendering if provided by some other view):defsample_view(request):assertrequest.method=='POST'assertrequest.is_ajax()form=MyUploadForm(request.POST,request.FILES)ifform.is_valid():url=form.form_valid(request)returnJsonResponse({'action':'redirect','url':url,})else:returnJsonResponse({'action':'replace','html':form.as_html(request),})InstallationInstall the package from Python Package Index running:pipinstalldjango-upload-formor from GitHub:pipinstallgit+https://github.com/morlandi/django-upload-form@masterthen add ‘upload_form’ to your INSTALLED_APPS:INSTALLED_APPS=[...'upload_form',]App SettingsSome settings are provided for optional customization.The library will search these settings in the following order:asDjango Constancedynamic settings (seehttps://github.com/jazzband/django-constance)failing that, in project’s settingsfailing that, a suitable “safe” default value is usedUPLOAD_FORM_MAX_FILE_SIZE_MB=12UPLOAD_FORM_ALLOWED_FILE_TYPES=".png .jpg .jpeg .gif"UPLOAD_FORM_PARALLEL_UPLOAD=False(experimental)or:CONSTANCE_CONFIG={...'UPLOAD_FORM_ALLOWED_FILE_TYPES':(".png .jpg .jpeg .gif","Tipi di files abilitati all'upload"),'UPLOAD_FORM_MAX_FILE_SIZE_MB':(12,'Dimensione massima files in upload (MB)'),'UPLOAD_FORM_PARALLEL_UPLOAD':(False,"Activate concurrent files upload"),}Using multiple upload forms in a single HTML pageThe technique used to collect files for upload is:render anhiddenfile input element with “multiple” attributeuse a label styled to look like a button, so people will realize they can click it to bring up the file selection dialogThis works, since pressing a label basically triggers the focus event for the bound input;
if it is a file input, it works out as a click event, resulting in opening a file browser.<inputstyle="display: none;"type="file"name="files[]"accept="{{accept}}"id="uploadform_file_input"multipleonchange="UploadForm.handleFiles(this.files)"><labelclass="button"for="uploadform_file_input"><imgsrc="{% static 'upload_form/icons/cloud-upload-svgrepo-com.svg' %}"alt="{% trans 'upload' %}"><p>{% trans 'Select some files' %}</p></label>Unfortunately, this means we had to assign a specific id to the <input> element;
which in turn means we can’t use two upload forms in a single HTML page.However, you can also implicitly bind a label to an input by inclusion, thus avoiding the id altoghether:<labelclass="button"><inputstyle="display: none;"type="file"name="files[]"accept="{{accept}}"multipleonchange="UploadForm.handleFiles(this.files)"><imgsrc="{% static 'upload_form/icons/cloud-upload-svgrepo-com.svg' %}"alt="{% trans 'upload' %}"><p>{% trans 'Select some files' %}</p></label>This fix has been added inv0.4.3.Example projectA simple Django project is available in folder ‘example’; use it as follows:# Move to the project foldercd./example# Install Django dependenciespipinstall-rrequirements.txt# Initialize database tablespythonmanage.pymigrate# Create a super-user for the admin:pythonmanage.pycreatesuperuser# Run the projectpythonmanage.pyrunserverthe visit eitherhttp://127.0.0.1:8000/orhttp://127.0.0.1:8000/admin/Sample usageTheupoad_formapp provides a sample test view which uploads multiple files
for illustration purposes.You can run, study, and possibly duplicate it for further customizations.To use it, add this to your main urls mapping:file urls.py:urlpatterns=[...path('upload_form/',include('upload_form.urls',namespace='upload_form')),...]then visit this url:http://127.0.0.1:8000/upload_form/test/Below is the source code of the whole test.file upload_form/views.pyfromdjango.shortcutsimportrendertry:fromdjango.urlsimportreverseexceptModuleNotFoundErrorase:# for Django < v1.10fromdjango.core.urlresolversimportreversefromdjango.httpimportJsonResponsefrom.formsimportUploadFormclassTestUploadForm(UploadForm):defform_valid(self,request):print("*")print("* TestUploadForm.form_valid() ...")print("* Here, we just log the list of received files;")print("* What you do with these files in a real project is entirely up to you.")print("*")self.dump()returnself.get_success_url(request)defget_success_url(self,request=None):return'/'defget_action(self,request=None):returnreverse('upload_form:test_view')deftest_view(request):ifrequest.method=='GET':form=TestUploadForm()else:form=TestUploadForm(request.POST,request.FILES)ifform.is_valid():url=form.form_valid(request)returnJsonResponse({'action':'redirect','url':url,})else:returnJsonResponse({'action':'replace','html':form.as_html(request),})returnrender(request,'upload_form/test_view.html',{'form':form,'form_as_html':form.as_html(request),})file templates/upload_form/test.html{% extends "base.html" %}
{% load static %}


{% block extrajs %}
    {{form.media}}
{% endblock %}


{% block content %}<divclass="container"><divclass="row"><divid="upload-box"class="text-center col-md-6 col-md-offset-3"style="">{{ form_as_html }}</div></div></div>{% endblock content %}Howto upload a videoclassMyUploadForm(UploadForm):...defget_accept(self,request=None):return'video/*'Other options to consider:- accept="video/*" capture=""       ... only from camera
- accept="video/*"                  ... either camera or albumHowto upload an image from the camera- accept="image/*" capture="camera"ScreenshotsLicenseThe app is intended to be open source.Feel free to use it we at your will with no restrictions at all.ReferencesHow To Make A Drag-and-Drop File Uploader With Vanilla JavaScriptMultiple File Upload InputStyling & Customizing File Inputs the Smart WayHow to set file input value when dropping file on page?A strategy for handling multiple file uploads using JavaScriptUse HTML5 to resize an image before uploadHow to package a Django app to be test-friendly?HTML Label name instead of ID?Historyv0.5.0typo fixesper-class “allowed_file_types” listbetter explanation of the difference between “accept” and “allowed_file_types”v0.4.3fix a js error when uploading non-image files; this was a regression introduced with image resizingfix to use multiple upload forms in a single HTML pagev0.4.2Include optional extra form fields in upload formv0.4.1Readme updated and fixedv0.4.0optionally resize images before uploadingparallel uploading (experimental)Example project addedsome miscellaneous improvements recovered from experimental branchesremoved dependency on sprintf js libraryv0.3.1fix Readme to keep twine happyv0.3.0published on PyPIv0.2.3prepare for publishing on PyPIv0.2.2improved Readmev0.2.1package renamed “django-upload-form” (was “django_upload_form”)cleanupimproved documentationv0.2.0fixes for Django 2.xuser can now add more file to the list before submissionv0.0.5green send buttonv0.0.4App settings removedv0.0.3Minor fixesv0.0.2Italian translation addedv0.0.1Basic release