In this tutorial we will explore how we can upload files in our project using Django.
Basic Walkthrough
Now whenever files are uploaded in the site they end up in request.FILES
and for developer it is a must to add attribute enctype="multipart/form-data"
in their HTML form, if developer fails to add the attribute then request.FILES
will be empty.
Also the form method should be the POST.
Inorder to handle uploaded files Django provides two model fields i.e., FileField and ImageField and files uploaded in FileField and ImageField are stored in the filesystem.
FielField and ImageField are added to database as VARCHAR and contains the reference to the file uploaded.
Incase these two fields are deleted from the database then only reference to the physical file is deleted.
The request.FILES mimics the behavior of dictionary and each key in request.FILES
is the name from <input type="file" name="" />
and each value of this dictionary like object is the instance of file uploaded.
In order to add file upload functionality you will have to set MEDIA_URL
and MEDIA_ROOT
in the settings.py file of the project.
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
For development environment you can use django.contrib.staticfiles.views.serve() view for uploaded files.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
............
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
For accessing the MEDIA_URL in the template django.template.context_processors.media
must be added context_processeors inside the TEMPLATES config.
Example of file upload
We will look into a sample file upload example.
demo_upload.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="dbfile">
<button type="submit">Upload</button>
</form>
{% if uploaded_file_url %}
<p>File upload location: <a href="{{ demo_file_url }}">{{ demo_file_url }}</a></p>
{% endif %}
<p><a href="{% url 'home' %}">Return to home</a></p>
{% endblock %}
demo_upload.py
from django.shortcuts import render
from django.conf import settings
from django.core.files.storage import FileSystemStorage
def simple_upload(request):
if request.method == 'POST' and request.FILES['myfile']:
myfile = request.FILES['dbfile']
fs = FileSystemStorage()
filename = fs.save(myfile.name, dbfile)
uploaded_file_url = fs.url(filename)
return render(request, 'core/demo_upload.html', {
'uploaded_file_url': uploaded_file_url
})
return render(request, 'core/demo_upload.html')
Upload files using Model Forms
Another convenient way to upload files is using model forms.
Now Model forms come with many features they help to validate, build the absolute url and helps in resolving file name conflicts.
models.py
from django.db import models
class Document(models.Model):
description = models.CharField(max_length=255, blank=True)
document = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
forms.py
from django import forms
from uploads.core.models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('description', 'document', )
views.py
def model_form_upload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('home')
else:
form = DocumentForm()
return render(request, 'core/demo_upload_using_model_form.html', {
'form': form
})
demo_upload_using_model_form.html
{% extends 'base.html' %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
<p><a href="{% url 'home' %}">Return to home</a></p>
{% endblock %}
Uploading multiple files
Incase you are fiddling with idea to upload multiple files using one form field then you have to set the multiple HTML attribute of field’s widget:
forms.py
from django import forms
class FileFieldForm(forms.Form):
file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
Next you have to override the post method of your FormView subclass to facilitate multiple file uploads:
views.py
from django.views.generic.edit import FormView
from .forms import FileFieldForm
class FileFieldView(FormView):
form_class = FileFieldForm
template_name = 'upload.html' # Replace with your template.
success_url = '...' # Replace with your URL or reverse().
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('file_field')
if form.is_valid():
for f in files:
... # Do something with each file.
return self.form_valid(form)
else:
return self.form_invalid(form)
I hope this piece of work will make your life easy when you integrate file upload option in your code.