Introduction

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.

What You Will Learn

By the end of this tutorial, you will understand:

Prerequisites

Before starting, you should already know:

1. Static Files vs Media Files

Before uploading files, you need to understand the difference between static files and media files.

Static files

These are files that belong to your project itself, such as:

Media files

These are files uploaded by users, such as:

In Django:

This tutorial focuses on media files.

2. File Upload Fields in Django

Django provides two main model fields for uploads:

FileField

Used for general files like:

ImageField

Used specifically for images like:

ImageField requires the Pillow library.

Install it with:

pip install Pillow

3. First Example: Uploading a Document

Let us begin with a simple model for uploaded documents.

models.py

from 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.title

Explanation

4. Example: Uploading an Image

Now let us create an image upload model.

models.py

from 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.title

Explanation

5. Configure Media Settings

To make file uploads work, you must configure media settings.

settings.py

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

What this means

So 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.jpg

6. Configure URLs for Media Files in Development

During development, Django can serve media files automatically.

Project urls.py

from 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)

Explanation

This tells Django:

This works in development only. In production, media files are usually served by Nginx, Apache, or cloud storage.

7. Make Migrations

After creating the model, run:

python manage.py makemigrations
python manage.py migrate

8. Create a ModelForm for Uploads

Django forms make uploads easier.

forms.py

from 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']

9. Create the Upload View

Function-Based View Example

views.py

from 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})

Very Important: request.FILES

When handling file uploads, you must include:

request.FILES

Without it, Django will not receive the uploaded file.

10. Create the Upload Template

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>

Very Important: enctype="multipart/form-data"

Without this, the browser will not send the file correctly.

This is one of the most common beginner mistakes.

11. Add the Upload URL

urls.py

from django.urls import path
from .views import upload_document

urlpatterns = [
    path('upload/', upload_document, name='upload_document'),
]

12. Display Uploaded Files

After uploading, you often want to show uploaded files in a list.

views.py

from 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.py

from 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 %}

13. Display Uploaded Images

For images, you can display them directly.

views.py

from 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 %}

14. Full Example: Image Upload App

models.py

from 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.title

forms.py

from django import forms
from .models import Photo

class PhotoForm(forms.ModelForm):
    class Meta:
        model = Photo
        fields = ['title', 'image']

views.py

from 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.py

from 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 %}

15. Organizing Uploaded Files

The upload_to option can be customized.

Simple folder

image = models.ImageField(upload_to='photos/')

Nested folder by year/month

image = models.ImageField(upload_to='photos/%Y/%m/')

This helps keep uploads organized.

Example stored path:

media/photos/2026/04/image.jpg

16. File Validation Basics

Sometimes you want to limit uploads.

For example:

You can do that with custom form validation.

Example: Only PDF files

forms.py

from 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 file

17. File Size Validation Example

from 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 file

18. Updating Models with Uploaded Files

If 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.

19. Deleting Uploaded 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:

20. Using File Uploads in Class-Based Views

You can also use generic views like CreateView.

Example

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')

Template

<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.

21. Common Beginner Mistakes

Forgetting request.FILES

Wrong:

form = DocumentForm(request.POST)

Correct:

form = DocumentForm(request.POST, request.FILES)

Forgetting enctype="multipart/form-data"

Without it, the file will not be uploaded.

Not configuring MEDIA_URL and MEDIA_ROOT

Without media settings, Django cannot handle uploaded files correctly.

Forgetting Pillow for ImageField

If you use ImageField, install Pillow:

pip install Pillow

Confusing static and media files

User uploads belong to media, not static.

22. Best Practices

23. Mini Project Example: User Profile Avatar

models.py

from 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

Template Example

{% 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.

24. Summary

In this tutorial, you learned that:

File uploads are an essential feature in many real Django projects, and once the basic configuration is correct, they are quite easy to manage.

 

25. Mini Quiz

1. Which field is used for general file uploads?

A. TextField
B. FileField
C. URLField
D. CharField

2. Which field is used for image uploads?

A. ImageField
B. PhotoField
C. PictureField
D. MediaField

3. What must be included in the form tag for file uploads?

A. method="get"
B. action="upload"
C. enctype="multipart/form-data"
D. target="_blank"

4. What must be passed to the form in the view?

A. request.DATA
B. request.FILES
C. request.MEDIA
D. request.UPLOADS

5. Which library is required for ImageField?

A. NumPy
B. Requests
C. Pillow
D. Pandas

26. What Comes Next?

The next ideal tutorial is:

Tutorial: User Authentication in Django
Subject: login, logout, signup, password management, and authentication basics.