Django Media Files

In the previous lesson we learned about Static Files — CSS, JS, and images that you, the developer, place in the project. But what about files uploaded by your website's users — like a profile photo, a student ID document, or a product image? These are called Media Files in Django, and they need a completely different setup from static files.

Static Files vs Media Files — The Key Difference

Before diving in, it is important to be clear on this difference:

  • Static Files: Created by the developer. Added to the project manually. Examples — your site's CSS, logo, and JavaScript files.
  • Media Files: Uploaded by users at runtime. Stored dynamically when someone submits a form. Examples — a user's profile picture, a teacher's uploaded assignment PDF.
Example: Think of a school notice board (static) versus a student submission tray (media). The notice board has posters pinned by the school (developer). The submission tray collects assignments from students (users) every day. Both are in the school, but they are managed very differently.

Configuring Media Files in settings.py

Open settings.py and add these two settings at the bottom:


import os

# The URL prefix that the browser uses to access uploaded files
MEDIA_URL = '/media/'

# The folder on your computer where uploaded files are physically saved
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
  • MEDIA_URL — The web address prefix for media files. When a user uploads a photo, its URL will look like /media/photos/profile.jpg.
  • MEDIA_ROOT — The actual folder on your server's hard drive where Django saves uploaded files.
Example: MEDIA_URL is the street address label (what the outside world uses to find the file). MEDIA_ROOT is the actual physical storage room (where Django saves the file on your computer). Both point to the same place, just from different perspectives.

Serving Media Files During Development

During development, Django does not serve media files automatically. You need to add a special URL rule in your project-level urls.py to make Django serve them while you are testing locally:


# myschool/urls.py

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('school.urls')),
]

# Add this at the end — only for development
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

This tells Django: "During development, if a request comes in for a /media/ URL, find the file in the MEDIA_ROOT folder and serve it."

Adding a File Upload Field to a Model

To allow users to upload files, add an ImageField or FileField to your model. Let us add a profile photo to the Student model:


from django.db import models

class Student(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    profile_photo = models.ImageField(upload_to='student_photos/', blank=True, null=True)
    document = models.FileField(upload_to='student_docs/', blank=True, null=True)

    def __str__(self):
        return self.name
  • ImageField — For image uploads (.jpg, .png, .gif, etc.). Requires the Pillow library.
  • FileField — For any type of file upload (.pdf, .docx, .csv, etc.).
  • upload_to — The subfolder inside MEDIA_ROOT where Django saves files for this field. Django creates it automatically.

Install Pillow (Required for ImageField)


pip install Pillow

Without Pillow installed, Django will raise an error when you try to use ImageField.

File Upload Folder Structure

After uploads happen, your project folder will look like this:


myschool/
├── manage.py
├── media/                        <-- Created automatically by Django
│   ├── student_photos/           <-- Comes from upload_to='student_photos/'
│   │   ├── rahul_photo.jpg
│   │   └── priya_photo.png
│   └── student_docs/             <-- Comes from upload_to='student_docs/'
│       └── rahul_marksheet.pdf
└── myschool/

Creating a Form for File Upload

Create a ModelForm for the Student model in forms.py:


from django import forms
from .models import Student

class StudentForm(forms.ModelForm):
    class Meta:
        model = Student
        fields = ['name', 'age', 'profile_photo', 'document']

Handling File Upload in a View

When a form includes a file upload, you must pass request.FILES along with request.POST to the form. Forgetting request.FILES is the most common beginner mistake:


from django.shortcuts import render, redirect
from .forms import StudentForm

def add_student(request):
    if request.method == 'POST':
        form = StudentForm(request.POST, request.FILES)  # <-- request.FILES is required
        if form.is_valid():
            form.save()
            return redirect('student-list')
    else:
        form = StudentForm()
    return render(request, 'add_student.html', {'form': form})

The Template for File Upload

The HTML form tag must include enctype="multipart/form-data". Without this attribute, the file will not be sent to Django at all:


<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Upload Student</button>
</form>
Example: enctype="multipart/form-data" is like choosing the right envelope for your parcel. A regular envelope (normal form) can only carry paper (text). A padded box with bubble wrap (multipart) is needed to safely carry a photo or document. Without the right packaging, the file never makes it to Django.

Displaying an Uploaded File in a Template

Once a file is uploaded, you can display it in a template using the .url attribute of the file field:


{% if student.profile_photo %}
    <img src="{{ student.profile_photo.url }}" alt="Profile Photo" width="150">
{% else %}
    <p>No photo uploaded.</p>
{% endif %}

{% if student.document %}
    <a href="{{ student.document.url }}">Download Document</a>
{% endif %}

The .url property returns the full web path to the file, like /media/student_photos/rahul_photo.jpg.

Using upload_to with Dynamic Folder Names

You can organize uploaded files into folders based on the record's data. For example, save each student's files in a folder named after their ID:


def student_upload_path(instance, filename):
    return f'students/{instance.name}/{filename}'

class Student(models.Model):
    name = models.CharField(max_length=100)
    profile_photo = models.ImageField(upload_to=student_upload_path)

Now files are saved as media/students/Rahul/photo.jpg instead of a flat folder — much easier to manage.

Run Migrations After Adding File Fields

After adding ImageField or FileField to a model, always run migrations:


python manage.py makemigrations
python manage.py migrate

Quick Recap

  • Add MEDIA_URL and MEDIA_ROOT in settings.py.
  • Serve media files in development by updating urls.py with static().
  • Use ImageField (install Pillow first) or FileField in your model.
  • Always pass request.FILES in the view when handling file upload forms.
  • Always add enctype="multipart/form-data" to the HTML form tag.
  • Use .url to display uploaded files in templates.

Leave a Comment

Your email address will not be published. Required fields are marked *