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_URLis the street address label (what the outside world uses to find the file).MEDIA_ROOTis 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
Pillowlibrary. - FileField — For any type of file upload (.pdf, .docx, .csv, etc.).
- upload_to — The subfolder inside
MEDIA_ROOTwhere 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_URLandMEDIA_ROOTinsettings.py. - Serve media files in development by updating
urls.pywithstatic(). - Use
ImageField(install Pillow first) orFileFieldin your model. - Always pass
request.FILESin the view when handling file upload forms. - Always add
enctype="multipart/form-data"to the HTML form tag. - Use
.urlto display uploaded files in templates.
