How to upload a file in Django? [closed]
Asked Answered
W

10

744

What is the minimal example code needed for a "hello world" app using Django 1.3, that enables the user to upload a file?

Whereof answered 3/5, 2011 at 15:17 Comment(0)
A
1364

Phew, Django documentation really does not have good example about this. I spent over 2 hours to dig up all the pieces to understand how this works. With that knowledge I implemented a project that makes possible to upload files and show them as list. To download source for the project, visit https://github.com/axelpale/minimal-django-file-upload-example or clone it:

> git clone https://github.com/axelpale/minimal-django-file-upload-example.git

Update 2013-01-30: The source at GitHub has also implementation for Django 1.4 in addition to 1.3. Even though there is few changes the following tutorial is also useful for 1.4.

Update 2013-05-10: Implementation for Django 1.5 at GitHub. Minor changes in redirection in urls.py and usage of url template tag in list.html. Thanks to hubert3 for the effort.

Update 2013-12-07: Django 1.6 supported at GitHub. One import changed in myapp/urls.py. Thanks goes to Arthedian.

Update 2015-03-17: Django 1.7 supported at GitHub, thanks to aronysidoro.

Update 2015-09-04: Django 1.8 supported at GitHub, thanks to nerogit.

Update 2016-07-03: Django 1.9 supported at GitHub, thanks to daavve and nerogit

Project tree

A basic Django 1.3 project with single app and media/ directory for uploads.

minimal-django-file-upload-example/
    src/
        myproject/
            database/
                sqlite.db
            media/
            myapp/
                templates/
                    myapp/
                        list.html
                forms.py
                models.py
                urls.py
                views.py
            __init__.py
            manage.py
            settings.py
            urls.py

1. Settings: myproject/settings.py

To upload and serve files, you need to specify where Django stores uploaded files and from what URL Django serves them. MEDIA_ROOT and MEDIA_URL are in settings.py by default but they are empty. See the first lines in Django Managing Files for details. Remember also set the database and add myapp to INSTALLED_APPS

...
import os

BASE_DIR = os.path.dirname(os.path.dirname(__file__))
...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'database.sqlite3'),
        'USER': '',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
...
INSTALLED_APPS = (
    ...
    'myapp',
)

2. Model: myproject/myapp/models.py

Next you need a model with a FileField. This particular field stores files e.g. to media/documents/2011/12/24/ based on current date and MEDIA_ROOT. See FileField reference.

# -*- coding: utf-8 -*-
from django.db import models

class Document(models.Model):
    docfile = models.FileField(upload_to='documents/%Y/%m/%d')

3. Form: myproject/myapp/forms.py

To handle upload nicely, you need a form. This form has only one field but that is enough. See Form FileField reference for details.

# -*- coding: utf-8 -*-
from django import forms

class DocumentForm(forms.Form):
    docfile = forms.FileField(
        label='Select a file',
        help_text='max. 42 megabytes'
    )

4. View: myproject/myapp/views.py

A view where all the magic happens. Pay attention how request.FILES are handled. For me, it was really hard to spot the fact that request.FILES['docfile'] can be saved to models.FileField just like that. The model's save() handles the storing of the file to the filesystem automatically.

# -*- coding: utf-8 -*-
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from myproject.myapp.models import Document
from myproject.myapp.forms import DocumentForm

def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = DocumentForm(request.POST, request.FILES)
        if form.is_valid():
            newdoc = Document(docfile = request.FILES['docfile'])
            newdoc.save()

            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('myapp.views.list'))
    else:
        form = DocumentForm() # A empty, unbound form

    # Load documents for the list page
    documents = Document.objects.all()

    # Render list page with the documents and the form
    return render_to_response(
        'myapp/list.html',
        {'documents': documents, 'form': form},
        context_instance=RequestContext(request)
    )

5. Project URLs: myproject/urls.py

Django does not serve MEDIA_ROOT by default. That would be dangerous in production environment. But in development stage, we could cut short. Pay attention to the last line. That line enables Django to serve files from MEDIA_URL. This works only in developement stage.

See django.conf.urls.static.static reference for details. See also this discussion about serving media files.

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = patterns('',
    (r'^', include('myapp.urls')),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

6. App URLs: myproject/myapp/urls.py

To make the view accessible, you must specify urls for it. Nothing special here.

# -*- coding: utf-8 -*-
from django.conf.urls import patterns, url

urlpatterns = patterns('myapp.views',
    url(r'^list/$', 'list', name='list'),
)

7. Template: myproject/myapp/templates/myapp/list.html

The last part: template for the list and the upload form below it. The form must have enctype-attribute set to "multipart/form-data" and method set to "post" to make upload to Django possible. See File Uploads documentation for details.

The FileField has many attributes that can be used in templates. E.g. {{ document.docfile.url }} and {{ document.docfile.name }} as in the template. See more about these in Using files in models article and The File object documentation.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Minimal Django File Upload Example</title>   
    </head>
    <body>
    <!-- List of uploaded documents -->
    {% if documents %}
        <ul>
        {% for document in documents %}
            <li><a href="{{ document.docfile.url }}">{{ document.docfile.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No documents.</p>
    {% endif %}

        <!-- Upload form. Note enctype attribute! -->
        <form action="{% url 'list' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            <p>{{ form.non_field_errors }}</p>
            <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
            <p>
                {{ form.docfile.errors }}
                {{ form.docfile }}
            </p>
            <p><input type="submit" value="Upload" /></p>
        </form>
    </body>
</html> 

8. Initialize

Just run syncdb and runserver.

> cd myproject
> python manage.py syncdb
> python manage.py runserver

Results

Finally, everything is ready. On default Django developement environment the list of uploaded documents can be seen at localhost:8000/list/. Today the files are uploaded to /path/to/myproject/media/documents/2011/12/17/ and can be opened from the list.

I hope this answer will help someone as much as it would have helped me.

Aluminize answered 17/12, 2011 at 1:59 Comment(23)
Found the location in django docs that shows file uploads. The example in this answer is excellent, but the info in the django docs will be kept up to date with new releases. docs.djangoproject.com/en/dev/topics/http/file-uploadsRattlesnake
@Rattlesnake Now there is also implementation for Django 1.4 at GitHub. We may see implementations for newer versions in the future :)Crinkly
The example doesn't work for Django "1.5". In the HTML {% url list %} becomes {% url "list" %}.Therefrom
@AkseliPalén could you also include a example for django admin in your exampleFit
om django.conf.urls.defaults import patterns, include, url from django.conf import settings from django.conf.urls.static import sHomophonous
How do you upload a file and other data from other fields in the form? Would you need to encode the other data in the POST URL as url parameters?Rhinarium
+1 instead of lines newdoc = Document(docfile = request.FILES['docfile']) newdoc.save() you can use form.save()Criminal
@WillianRibeiro Changing the code of an answer, especially old like this one, is not a edit to make. Instead, comment on the change and if the author feels it is needed, he/she will change it.Clemons
Top top answer! really helped a lot as I am also in the very beginning stages of django. I see that the database is being specified, but I'm still trying to figure out if the database is being used? Looking for a simple example on how to read from a databaseMath
@AkseliPalén I get "NoReverseMatch at /list/" why is that so? I copied your code ditto but it does not work like you have mentioned.Quantic
@AkseliPalén using list() as a user-defined function is not a good practice, am I right? Since it shadows the built-in function. Could that be the reason it is not working or what?Quantic
@MatthieuRiegler I had to use {%url 'list' %} as well and I am using Django 1.7.1..Thank you..my problem got solved.Quantic
Thank you very much . It really works for me. However, for the upcoming viewers, you should check the code in gitHub for the best compatibility with new versions of Python and Django. For example, the the views.py, render_to_response() should be replaced with render(request,...,) to avoid the CSRF error. Cheers.Mystagogue
it is possible doing this without FORMS?Indomitable
Is the uploaded file being stored in the database? It seems that it's just being saved in some folder. How do we store it in a database?Brueghel
@ArkyaChatterjee In the database, a row is created that represents the file. The row does not contain the binary data of the file but only stores the location of the file on the local server. It is almost always better to store uploaded files into a local directory instead of a database because databases are usually not designed for serving files. There is multiple tools to share those static files, depending your tech stack. See this django tutorial about serving static files.Crinkly
When I try to upload a file, I get an OSError: [Errno 13] Permission denied: '/media/documents/test_file_PeKLdiC.csv' . Do I need to give the user ownership somehow? E.g. sudo chown django:django /media/documents -R or something like this?Laforge
Can the file be .zip, or other compressed files?Crosseye
I want to scp to the media dir as alternative to the upload. How would I do this regarding the model entry? I understood that the upload will do the file storage AND the db entry. When doing scp I need to manually fill the db? Is it complicated?Retainer
IMHO A good idea would be to integrate this into Cookiecutter Django template maker.Buchner
What happens if you are not creating a new model instance? but just updating the filefield on an existing one?Jezreel
It works for me. Very helpful to Django newbie like me.Alongshore
Update - Now works with Django 3 as wellPlimsoll
C
95

Demo

See the github repo, works with Django 3

A minimal Django file upload example

1. Create a django project

Run startproject::

$ django-admin.py startproject sample

now a folder(sample) is created.

2. create an app

Create an app::

$ cd sample
$ python manage.py startapp uploader

Now a folder(uploader) with these files are created::

uploader/
  __init__.py
  admin.py
  app.py
  models.py
  tests.py
  views.py
  migrations/
    __init__.py

3. Update settings.py

On sample/settings.py add 'uploader' to INSTALLED_APPS and add MEDIA_ROOT and MEDIA_URL, ie::

INSTALLED_APPS = [
    'uploader',
    ...<other apps>...      
]

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

4. Update urls.py

in sample/urls.py add::

...<other imports>...
from django.conf import settings
from django.conf.urls.static import static
from uploader import views as uploader_views

urlpatterns = [
    ...<other url patterns>...
    path('', uploader_views.UploadView.as_view(), name='fileupload'),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5. Update models.py

update uploader/models.py::

from django.db import models
class Upload(models.Model):
    upload_file = models.FileField()    
    upload_date = models.DateTimeField(auto_now_add =True)

6. Update views.py

update uploader/views.py::

from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .models import Upload
class UploadView(CreateView):
    model = Upload
    fields = ['upload_file', ]
    success_url = reverse_lazy('fileupload')
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['documents'] = Upload.objects.all()
        return context

7. create templates

Create a folder sample/uploader/templates/uploader

Create a file upload_form.html ie sample/uploader/templates/uploader/upload_form.html::

<div style="padding:40px;margin:40px;border:1px solid #ccc">
    <h1>Django File Upload</h1>
    <form method="post" enctype="multipart/form-data">
      {% csrf_token %}
      {{ form.as_p }}
      <button type="submit">Submit</button>
    </form><hr>
    <ul>
    {% for document in documents %}
        <li>
            <a href="{{ document.upload_file.url }}">{{ document.upload_file.name }}</a>
            <small>({{ document.upload_file.size|filesizeformat }}) - {{document.upload_date}}</small>
        </li>
    {% endfor %}
    </ul>
</div>

8. Syncronize database

Syncronize database and runserver::

$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver

visit http://localhost:8000/

Criminal answered 5/6, 2014 at 17:10 Comment(8)
perfect except for the last line - should be localhost.com:8000/upload> This worked for django 1.6 and Python 3.3.Amory
+1 for reusable django app design patternGoliard
Akseli used a FileField while suhail used an ImageField, could someone please explain the choices?Laminous
@dtgq I updated the answer to use with FileField. ImageField must for image only upload. the update will work with Django 1.11.Criminal
tested on Django 2.0 and worked perfectlyMicrophone
This is working fineFootcloth
Worked for me in Django 4.1 except that in step one, I ran this: django-admin startproject sampleGable
@AndrewKelley i think both command django-admin and django-admin.py will work.Criminal
P
93

Generally speaking when you are trying to 'just get a working example' it is best to 'just start writing code'. There is no code here to help you with, so it makes answering the question a lot more work for us.

If you want to grab a file, you need something like this in an html file somewhere:

<form method="post" enctype="multipart/form-data">
    <input type="file" name="myfile" />
    <input type="submit" name="submit" value="Upload" />
</form>

That will give you the browse button, an upload button to start the action (submit the form) and note the enctype so Django knows to give you request.FILES

In a view somewhere you can access the file with

def myview(request):
    request.FILES['myfile'] # this is my file

There is a huge amount of information in the file upload docs

I recommend you read the page thoroughly and just start writing code - then come back with examples and stack traces when it doesn't work.

Platto answered 3/5, 2011 at 15:25 Comment(6)
Thanks Henry. Actually I've read the docs and have written some code but since the docs has some gaps (for example "from somewhere import handle_uploaded_file") and my code was flawed, thought that it would be much better if I could start from a working example.Whereof
Agree with qliq. A simple working example is the most efficient way to get newbies going, not docsVarion
The enctype="multipart/form-data" what I needed to make this work, thanks!Sauceda
Just don't miss the {% csrf_token %} within the form tags.Morrissey
it is possible doing this WITHOUT FORMS FROM FORMS.PY?Indomitable
MultiValueDictKeyError at /imsearch error.Witchcraft
R
33

I must say I find the documentation at django confusing. Also for the simplest example why are forms being mentioned? The example I got to work in the views.py is :-

for key, file in request.FILES.items():
    path = file.name
    dest = open(path, 'w')
    if file.multiple_chunks:
        for c in file.chunks():
            dest.write(c)
    else:
        dest.write(file.read())
    dest.close()

The html file looks like the code below, though this example only uploads one file and the code to save the files handles many :-

<form action="/upload_file/" method="post" enctype="multipart/form-data">{% csrf_token %}
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>

These examples are not my code, they have been optained from two other examples I found. I am a relative beginner to django so it is very likely I am missing some key point.

Rede answered 7/6, 2012 at 12:11 Comment(2)
+1 for not using a FileField and a model.Form. For beginners (and for trivial tasks), manual processing of uploaded files like shown above is less confusing.Galactometer
dest = open(path, 'wb') when file write with bytesDiella
R
24

I also had the similar requirement. Most of the examples on net are asking to create models and create forms which I did not wanna use. Here is my final code.

if request.method == 'POST':
    file1 = request.FILES['file']
    contentOfFile = file1.read()
    if file1:
        return render(request, 'blogapp/Statistics.html', {'file': file1, 'contentOfFile': contentOfFile})

And in HTML to upload I wrote:

{% block content %}
    <h1>File content</h1>
    <form action="{% url 'blogapp:uploadComplete'%}" method="post" enctype="multipart/form-data">
         {% csrf_token %}
        <input id="uploadbutton" type="file" value="Browse" name="file" accept="text/csv" />
        <input type="submit" value="Upload" />
    </form>
    {% endblock %}

Following is the HTML which displays content of file:

{% block content %}
    <h3>File uploaded successfully</h3>
    {{file.name}}
    </br>content = {{contentOfFile}}
{% endblock %}
Reluctant answered 10/11, 2014 at 2:16 Comment(1)
good because sometimes one just wants to use the contents of the file not save the upload...Counterfactual
T
17

Extending on Henry's example:

import tempfile
import shutil

FILE_UPLOAD_DIR = '/home/imran/uploads'

def handle_uploaded_file(source):
    fd, filepath = tempfile.mkstemp(prefix=source.name, dir=FILE_UPLOAD_DIR)
    with open(filepath, 'wb') as dest:
        shutil.copyfileobj(source, dest)
    return filepath

You can call this handle_uploaded_file function from your view with the uploaded file object. This will save the file with a unique name (prefixed with filename of the original uploaded file) in filesystem and return the full path of saved file. You can save the path in database, and do something with the file later.

Touber answered 3/5, 2011 at 15:42 Comment(4)
Imran, I tried your code on my view but got this error: 'WSGIRequest' object has no attribute 'name'.Whereof
Pass the uploaded file object (request.FILES['myfile']) to handle_uploaded_file, not the request itself.Touber
Could I save it directly to the database ? #24705746Tallbott
By using prefix=source.name it added extra characters at the end of file, messing up with file extension. E.g. upload.csv got changed to upload.csv5334. Changing it to suffix=source.name fixed it for me.Montford
D
13

Here it may helps you: create a file field in your models.py

For uploading the file(in your admin.py):

def save_model(self, request, obj, form, change):
    url = "http://img.youtube.com/vi/%s/hqdefault.jpg" %(obj.video)
    url = str(url)

    if url:
        temp_img = NamedTemporaryFile(delete=True)
        temp_img.write(urllib2.urlopen(url).read())
        temp_img.flush()
        filename_img = urlparse(url).path.split('/')[-1]
        obj.image.save(filename_img,File(temp_img)

and use that field in your template also.

Desirable answered 20/10, 2013 at 10:39 Comment(3)
This is useful, when you have to manually temper with files you want to save. If so, you may also need this section: docs.djangoproject.com/en/dev/topics/files/#the-file-objectAche
this is very useful, and it works. I have a question: how does django that the view is associated to the upload_form.html file? I don't see anything joining them...Okajima
got it... explained here: docs.djangoproject.com/en/3.1/ref/class-based-views/…Okajima
R
11

You can refer to server examples in Fine Uploader, which has django version. https://github.com/FineUploader/server-examples/tree/master/python/django-fine-uploader

It's very elegant and most important of all, it provides featured js lib. Template is not included in server-examples, but you can find demo on its website. Fine Uploader: http://fineuploader.com/demos.html

django-fine-uploader

views.py

UploadView dispatches post and delete request to respective handlers.

class UploadView(View):

    @csrf_exempt
    def dispatch(self, *args, **kwargs):
        return super(UploadView, self).dispatch(*args, **kwargs)

    def post(self, request, *args, **kwargs):
        """A POST request. Validate the form and then handle the upload
        based ont the POSTed data. Does not handle extra parameters yet.
        """
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_upload(request.FILES['qqfile'], form.cleaned_data)
            return make_response(content=json.dumps({ 'success': True }))
        else:
            return make_response(status=400,
                content=json.dumps({
                    'success': False,
                    'error': '%s' % repr(form.errors)
                }))

    def delete(self, request, *args, **kwargs):
        """A DELETE request. If found, deletes a file with the corresponding
        UUID from the server's filesystem.
        """
        qquuid = kwargs.get('qquuid', '')
        if qquuid:
            try:
                handle_deleted_file(qquuid)
                return make_response(content=json.dumps({ 'success': True }))
            except Exception, e:
                return make_response(status=400,
                    content=json.dumps({
                        'success': False,
                        'error': '%s' % repr(e)
                    }))
        return make_response(status=404,
            content=json.dumps({
                'success': False,
                'error': 'File not present'
            }))

forms.py

class UploadFileForm(forms.Form):

    """ This form represents a basic request from Fine Uploader.
    The required fields will **always** be sent, the other fields are optional
    based on your setup.
    Edit this if you want to add custom parameters in the body of the POST
    request.
    """
    qqfile = forms.FileField()
    qquuid = forms.CharField()
    qqfilename = forms.CharField()
    qqpartindex = forms.IntegerField(required=False)
    qqchunksize = forms.IntegerField(required=False)
    qqpartbyteoffset = forms.IntegerField(required=False)
    qqtotalfilesize = forms.IntegerField(required=False)
    qqtotalparts = forms.IntegerField(required=False)
Retroflexion answered 7/4, 2015 at 4:35 Comment(0)
L
8

Not sure if there any disadvantages to this approach but even more minimal, in views.py:

entry = form.save()

# save uploaded file
if request.FILES['myfile']:
    entry.myfile.save(request.FILES['myfile']._name, request.FILES['myfile'], True)
Lethe answered 19/5, 2011 at 13:0 Comment(0)
A
0

I faced the similar problem, and solved by django admin site.

# models
class Document(models.Model):
    docfile = models.FileField(upload_to='documents/Temp/%Y/%m/%d')

    def doc_name(self):
        return self.docfile.name.split('/')[-1] # only the name, not full path

# admin
from myapp.models import Document
class DocumentAdmin(admin.ModelAdmin):
    list_display = ('doc_name',)
admin.site.register(Document, DocumentAdmin)
Armindaarming answered 14/5, 2014 at 6:58 Comment(1)
[enter link description here][1] [enter link description here][2] [1]: youtu.be/0tNZB3dyopY [2]: youtu.be/klhMYMc3PlYFite

© 2022 - 2024 — McMap. All rights reserved.