Tech Blog

Open drawer in file cabinet

Multiple File Uploads in Django

Recently updated on

Here is a quick example of how to add a multiple file form to your Django application.  Most multiple-upload front-ends were created without Django in mind, so interfacing with tools is not always straightforward as it might be with a different language.  Thankfully, there are a couple of Django libraries and tutorials that help facilitate this interaction.  This example discusses one solution and by tweaking the information here, you should be able to adapt it to your needs.
 
 This example is based on the work and repo provided by Sigurd Gartmann and uses the jQuery File Upload Plugin.  Included in Sigurd’s repository is a skeleton example project. This tutorial will focus on expanding the example to demonstrate how it can be adapted to support your existing models.

Without overriding any javascript, the jQuery File Upload Plugin will only pass the file object back to the server during submission.  This works great for simple solutions such as the model in the example, but suppose we have a slightly more complex model structure.

For example, we will alter the current Picture model to include a few additional fields and requirements.  For our requirements, each file must be associated with a User (built in user).

Now our models.py looks like:

from django.db import models
from django.contrib.auth.models import User

class Picture(models.Model):

    # This is a small demo using FileField instead of ImageField, not
    # depending on PIL. You will probably want ImageField in your app.
    file = models.FileField(upload_to="pictures")
    slug = models.SlugField(max_length=50, blank=True)
    creator = models.ForeignKey(User)

    def __unicode__(self):
        return self.file.name

    @models.permalink
    def get_absolute_url(self):
        return ('upload-new', )

    def save(self, *args, **kwargs):
        self.slug = self.file.name
        super(Picture, self).save(*args, **kwargs)

If we try to upload files now, the form will not validate and the object will not be created.  By overriding the form_invalid function of our PictureCreateView, we can find the following information:  

[25/Jun/2012 12:20:52] "GET /upload/new/add/ HTTP/1.1" 200 9309

<ul class="errorlist"><li>creator<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
 And as expected, we can see that the form did not validate because all the required elements of the model were not present in the form.  At this point, we have two options.  We can either edit the provided javascript to include a request field or we can create a new view to handle the upload process.

For our purpose, let’s create a custom view to handle our new requirements. In our views.py let’s add a new function called multiple_uploader.  I will use a function based view.  Because the jQuery provided uses json to communicate between the client and server, we need to include that in our request response from the server.

def multiple_uploader(request):
    if request.POST:
        if request.FILES == None:
            raise Http404("No objects uploaded")
        f = request.FILES['file']

        a = Picture()
        a.creator = request.user
        a.file.save(f.name, f)
        a.save()

        result = [ {'name': f.name,
                       'size': f.size,
                       'url': a.file.url,
                       },]

        response_data = simplejson.dumps(result)
        if "application/json" in request.META['HTTP_ACCEPT_ENCODING']:
            mimetype = 'application/json'
        else:
            mimetype = 'text/plain'
        return HttpResponse(response_data, mimetype=mimetype)
    else:
        return HttpResponse('Only POST accepted')

Now we update our fileupload.urls to include the new function:

from django.conf.urls.defaults import *
from fileupload.views import PictureCreateView, PictureDeleteView, multiple_uploader


urlpatterns = patterns('',
    (r'^new/$', PictureCreateView.as_view(), {}, 'upload-new'),
    (r'^new/add/$', multiple_uploader, {}, 'upload-add'),
    (r'^delete/(?P<pk>\d+)$', PictureDeleteView.as_view(), {}, 'upload-delete'),
)

And finally, update the action of ‘fileupload’ form in in the fileupload/picture_form.html template.

<form id="fileupload" method="post" action="./add/" enctype="multipart/form-data">

Because we require an authenticated user to upload a file, we need to enable the admin so that we can log in since this example does not include a login page.  If we do not log in, the uploader will simply display internal service errors whenever we try to upload.

Then we just sync the database and start up the server and we have a custom view for handling our multiple file uploads and an example of how to add information not included with the built in form.

Make sure to log in to the admin before trying to upload files.

Try it on your own.  Git the source from here.

Comments

  1. Namju on 06/18/2016 12:25 p.m.

    Very Good.
    Thanks

Comments are closed.