Many real Django applications allow users to upload files. A blog may allow image uploads, a school platform may accept PDF assignments, a profile page may allow avatars, and an admin dashboard may manage documents.
Django makes file uploads straightforward, but there are a few important things you must understand:
In this tutorial, you will learn how to upload images and documents in Django step by step.
By the end of this tutorial, you will understand:
FileField and ImageFieldMEDIA_URL and MEDIA_ROOTBefore starting, you should already know:
Before uploading files, you need to understand the difference between static files and media files.
These are files that belong to your project itself, such as:
These are files uploaded by users, such as:
In Django:
This tutorial focuses on media files.
Django provides two main model fields for uploads:
FileFieldUsed for general files like:
ImageFieldUsed specifically for images like:
ImageField requires the Pillow library.
Install it with:
pip install PillowLet us begin with a simple model for uploaded documents.
models.pyfrom django.db import models
class Document(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titletitle stores the document namefile stores the uploaded fileupload_to='documents/' means files will be stored inside a documents/ folderuploaded_at stores the upload timeNow let us create an image upload model.
models.pyfrom django.db import models
class Photo(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='photos/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleImageField works like FileField, but for imagesupload_to='photos/' stores images in a photos/ folderTo make file uploads work, you must configure media settings.
settings.pyMEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'MEDIA_URL is the URL prefix used to access uploaded filesMEDIA_ROOT is the folder where uploaded files are stored on diskSo if a user uploads an image called cat.jpg, Django may store it like this:
media/photos/cat.jpg
and it may be accessible through:
/media/photos/cat.jpgDuring development, Django can serve media files automatically.
urls.pyfrom django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('your_app.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)This tells Django:
DEBUG=TrueMEDIA_ROOTMEDIA_URLThis works in development only. In production, media files are usually served by Nginx, Apache, or cloud storage.
After creating the model, run:
python manage.py makemigrations
python manage.py migrateDjango forms make uploads easier.
forms.pyfrom django import forms
from .models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'file']For images:
from django import forms
from .models import Photo
class PhotoForm(forms.ModelForm):
class Meta:
model = Photo
fields = ['title', 'image']views.pyfrom django.shortcuts import render, redirect
from .forms import DocumentForm
def upload_document(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('document_list')
else:
form = DocumentForm()
return render(request, 'uploads/upload_document.html', {'form': form})request.FILESWhen handling file uploads, you must include:
request.FILESWithout it, Django will not receive the uploaded file.
templates/uploads/upload_document.html<h1>Upload a Document</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
enctype="multipart/form-data"Without this, the browser will not send the file correctly.
This is one of the most common beginner mistakes.
urls.pyfrom django.urls import path
from .views import upload_document
urlpatterns = [
path('upload/', upload_document, name='upload_document'),
]After uploading, you often want to show uploaded files in a list.
views.pyfrom django.shortcuts import render
from .models import Document
def document_list(request):
documents = Document.objects.all().order_by('-uploaded_at')
return render(request, 'uploads/document_list.html', {'documents': documents})urls.pyfrom django.urls import path
from .views import upload_document, document_list
urlpatterns = [
path('upload/', upload_document, name='upload_document'),
path('documents/', document_list, name='document_list'),
]templates/uploads/document_list.html<h1>Uploaded Documents</h1>
{% for document in documents %}
<p>
{{ document.title }} -
<a href="{{ document.file.url }}">Download</a>
</p>
{% empty %}
<p>No documents uploaded yet.</p>
{% endfor %}
For images, you can display them directly.
views.pyfrom django.shortcuts import render
from .models import Photo
def photo_list(request):
photos = Photo.objects.all().order_by('-uploaded_at')
return render(request, 'uploads/photo_list.html', {'photos': photos})templates/uploads/photo_list.html<h1>Photo Gallery</h1>
{% for photo in photos %}
<h3>{{ photo.title }}</h3>
<img src="{{ photo.image.url }}" alt="{{ photo.title }}" width="300">
{% empty %}
<p>No photos uploaded yet.</p>
{% endfor %}models.pyfrom django.db import models
class Photo(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='photos/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleforms.pyfrom django import forms
from .models import Photo
class PhotoForm(forms.ModelForm):
class Meta:
model = Photo
fields = ['title', 'image']views.pyfrom django.shortcuts import render, redirect
from .forms import PhotoForm
from .models import Photo
def upload_photo(request):
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('photo_list')
else:
form = PhotoForm()
return render(request, 'uploads/upload_photo.html', {'form': form})
def photo_list(request):
photos = Photo.objects.all().order_by('-uploaded_at')
return render(request, 'uploads/photo_list.html', {'photos': photos})urls.pyfrom django.urls import path
from .views import upload_photo, photo_list
urlpatterns = [
path('photos/upload/', upload_photo, name='upload_photo'),
path('photos/', photo_list, name='photo_list'),
]upload_photo.html<h1>Upload a Photo</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload Photo</button>
</form>photo_list.html<h1>All Photos</h1>
{% for photo in photos %}
<h3>{{ photo.title }}</h3>
<img src="{{ photo.image.url }}" alt="{{ photo.title }}" width="250">
{% empty %}
<p>No photos uploaded yet.</p>
{% endfor %}The upload_to option can be customized.
image = models.ImageField(upload_to='photos/')image = models.ImageField(upload_to='photos/%Y/%m/')This helps keep uploads organized.
Example stored path:
media/photos/2026/04/image.jpgSometimes you want to limit uploads.
For example:
You can do that with custom form validation.
forms.pyfrom django import forms
from .models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'file']
def clean_file(self):
file = self.cleaned_data['file']
if not file.name.endswith('.pdf'):
raise forms.ValidationError('Only PDF files are allowed.')
return filefrom django import forms
from .models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'file']
def clean_file(self):
file = self.cleaned_data['file']
max_size = 5 * 1024 * 1024 # 5 MB
if file.size > max_size:
raise forms.ValidationError('File size must be under 5 MB.')
return fileIf you edit an object with a file field, Django allows replacing the file.
Example:
You can handle this using UpdateView or a custom edit view just like any other form, but still remember request.FILES.
If you delete a model instance, the database record is deleted, but the file on disk may remain unless you handle cleanup.
For beginners, it is enough to know this behavior exists.
In larger projects, developers often:
You can also use generic views like CreateView.
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .models import Photo
class PhotoCreateView(CreateView):
model = Photo
fields = ['title', 'image']
template_name = 'uploads/upload_photo.html'
success_url = reverse_lazy('photo_list')
<h1>Upload Photo</h1>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
Class-based views make uploads cleaner when building CRUD pages.
request.FILESWrong:
form = DocumentForm(request.POST)Correct:
form = DocumentForm(request.POST, request.FILES)enctype="multipart/form-data"Without it, the file will not be uploaded.
MEDIA_URL and MEDIA_ROOTWithout media settings, Django cannot handle uploaded files correctly.
ImageFieldIf you use ImageField, install Pillow:
pip install PillowUser uploads belong to media, not static.
ImageField for images and FileField for general filesupload_tomodels.pyfrom django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
def __str__(self):
return self.user.username{% if profile.avatar %}
<img src="{{ profile.avatar.url }}" alt="Avatar" width="150">
{% else %}
<p>No avatar uploaded.</p>
{% endif %}This is one of the most common real-world uses of file uploads in Django.
In this tutorial, you learned that:
FileField and ImageFieldMEDIA_URL and MEDIA_ROOT must be configuredrequest.FILESenctype="multipart/form-data".urlFile uploads are an essential feature in many real Django projects, and once the basic configuration is correct, they are quite easy to manage.
A. TextField
B. FileField
C. URLField
D. CharField
A. ImageField
B. PhotoField
C. PictureField
D. MediaField
A. method="get"
B. action="upload"
C. enctype="multipart/form-data"
D. target="_blank"
A. request.DATA
B. request.FILES
C. request.MEDIA
D. request.UPLOADS
ImageField?A. NumPy
B. Requests
C. Pillow
D. Pandas
The next ideal tutorial is:
Tutorial: User Authentication in Django
Subject: login, logout, signup, password management, and authentication basics.