Skip to main content

Django Media Files

Media files are user-uploaded content such as images, videos, PDF documents, and other file types that are part of a website's dynamic content. Unlike static files (CSS, JavaScript, etc.), media files are typically uploaded during the application's runtime rather than being part of the codebase itself.

Introduction to Django Media Files​

In web applications, particularly those that involve user-generated content, you'll need to handle file uploads. Django provides a streamlined way to manage these uploads through its media file handling system. This system allows you to:

  • Upload files from forms
  • Store them in a designated location on your server
  • Serve these files to users when requested
  • Process or validate files before storage

Let's learn how to properly configure and use media files in your Django projects.

Setting Up Media Files in Django​

1. Configure Settings​

First, you need to configure your settings.py file to specify where uploaded files should be stored:

python
# settings.py

# Base directory for media files
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# URL that handles the media served from MEDIA_ROOT
MEDIA_URL = '/media/'

Here's what these settings mean:

  • MEDIA_ROOT: The absolute filesystem path to the directory where user-uploaded files will be stored.
  • MEDIA_URL: The URL prefix that will be used for serving media files.

2. Configure URLs for Development​

During development, you'll need to configure Django to serve media files, as the development server doesn't serve media files by default:

python
# urls.py (project level)
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('myapp.urls')),
# other URL patterns...
]

# Add this conditional statement at the end of the file
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
warning

The approach above is only for development. In production, you should configure your web server (Nginx, Apache, etc.) to serve media files directly.

Creating File Upload Forms​

To enable users to upload files, you'll need to create a form that includes a file field.

Model with FileField or ImageField​

python
# models.py
from django.db import models

class Document(models.Model):
title = models.CharField(max_length=200)
# For any file type
file = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.title

class Profile(models.Model):
name = models.CharField(max_length=100)
# Specifically for images
avatar = models.ImageField(upload_to='avatars/')

def __str__(self):
return self.name

The upload_to parameter specifies a subdirectory within MEDIA_ROOT where files will be uploaded.

Creating a Form​

python
# forms.py
from django import forms
from .models import Document, Profile

class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ('title', 'file')

class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('name', 'avatar')

View for Handling Uploads​

python
# views.py
from django.shortcuts import render, redirect
from .forms import DocumentForm
from .models import Document

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, 'upload_document.html', {'form': form})

def document_list(request):
documents = Document.objects.all()
return render(request, 'document_list.html', {'documents': documents})
tip

Notice that for file uploads, you need to pass both request.POST and request.FILES to the form.

Template for File Upload​

html
<!-- upload_document.html -->
{% extends 'base.html' %}

{% block content %}
<h2>Upload a Document</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
{% endblock %}
important

The enctype="multipart/form-data" attribute is crucial for file uploads!

Template for Displaying Uploaded Files​

html
<!-- document_list.html -->
{% extends 'base.html' %}

{% block content %}
<h2>Documents</h2>
<ul>
{% for document in documents %}
<li>
<a href="{{ document.file.url }}">{{ document.title }}</a>
<small>Uploaded at: {{ document.uploaded_at }}</small>
</li>
{% empty %}
<li>No documents uploaded yet.</li>
{% endfor %}
</ul>
<a href="{% url 'upload_document' %}">Upload a new document</a>
{% endblock %}

Advanced Media File Handling​

File Validation​

You can validate files before saving them:

python
# forms.py
from django import forms
from django.core.exceptions import ValidationError
from .models import Document

def validate_file_size(value):
filesize = value.size

if filesize > 5 * 1024 * 1024: # 5MB
raise ValidationError("The maximum file size that can be uploaded is 5MB")
return value

class DocumentForm(forms.ModelForm):
file = forms.FileField(validators=[validate_file_size])

class Meta:
model = Document
fields = ('title', 'file')

Image Processing with Pillow​

For image processing, you'll need the Pillow library:

bash
pip install Pillow

Example of resizing an image before saving:

python
# models.py
from django.db import models
from PIL import Image
import os

class Profile(models.Model):
name = models.CharField(max_length=100)
avatar = models.ImageField(upload_to='avatars/')

def save(self, *args, **kwargs):
super().save(*args, **kwargs)

# Resize the image if needed
if self.avatar:
img = Image.open(self.avatar.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.avatar.path)

Organizing Media Files​

For larger applications, organize uploads in structured directories:

python
# models.py
def user_directory_path(instance, filename):
# Files will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return f'user_{instance.user.id}/{filename}'

class UserDocument(models.Model):
user = models.ForeignKey('auth.User', on_delete=models.CASCADE)
document = models.FileField(upload_to=user_directory_path)
uploaded_at = models.DateTimeField(auto_now_add=True)

Let's build a simple photo gallery where users can upload images and categorize them:

Models​

python
# models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
name = models.CharField(max_length=100)

def __str__(self):
return self.name

class Meta:
verbose_name_plural = "Categories"

class Photo(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
image = models.ImageField(upload_to='photos/%Y/%m/%d/')
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
upload_date = models.DateTimeField(auto_now_add=True)

def __str__(self):
return self.title

Forms​

python
# forms.py
from django import forms
from .models import Photo, Category

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

Views​

python
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import PhotoForm
from .models import Photo, Category

@login_required
def upload_photo(request):
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
photo = form.save(commit=False)
photo.uploaded_by = request.user
photo.save()
return redirect('gallery')
else:
form = PhotoForm()

return render(request, 'upload_photo.html', {'form': form})

def gallery(request):
categories = Category.objects.all()
category_id = request.GET.get('category')

if category_id:
photos = Photo.objects.filter(category_id=category_id)
else:
photos = Photo.objects.all()

context = {
'categories': categories,
'photos': photos
}
return render(request, 'gallery.html', context)

Templates​

html
<!-- upload_photo.html -->
{% extends 'base.html' %}

{% block content %}
<div class="container mt-5">
<h2>Upload a Photo</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div>
{% endblock %}
html
<!-- gallery.html -->
{% extends 'base.html' %}

{% block content %}
<div class="container mt-5">
<h1>Photo Gallery</h1>

<div class="row mb-3">
<div class="col-md-3">
<div class="card">
<div class="card-header">
Categories
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">
<a href="{% url 'gallery' %}">All</a>
</li>
{% for category in categories %}
<li class="list-group-item">
<a href="{% url 'gallery' %}?category={{ category.id }}">{{ category.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>

<div class="col-md-9">
<div class="row">
{% for photo in photos %}
<div class="col-md-4 mb-4">
<div class="card">
<img src="{{ photo.image.url }}" class="card-img-top" alt="{{ photo.title }}">
<div class="card-body">
<h5 class="card-title">{{ photo.title }}</h5>
<p class="card-text">{{ photo.description|truncatechars:100 }}</p>
<p class="text-muted">Uploaded by: {{ photo.uploaded_by.username }}</p>
</div>
</div>
</div>
{% empty %}
<div class="col-12">
<p>No photos found.</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}

URLs​

python
# urls.py
from django.urls import path
from . import views

urlpatterns = [
path('gallery/', views.gallery, name='gallery'),
path('upload/', views.upload_photo, name='upload_photo'),
]

Media Files in Production​

For a production environment, you should not rely on Django to serve media files. Instead:

  1. Configure your web server (Nginx, Apache) to serve files from your MEDIA_ROOT
  2. Consider using a CDN (Content Delivery Network) for better performance
  3. Implement security measures to protect sensitive uploads

Example Nginx configuration:

nginx
server {
# ...other configurations...

location /media/ {
alias /path/to/your/media/folder/;
}

# ...other configurations...
}

Security Considerations​

When dealing with user uploaded files, keep these security considerations in mind:

  1. Validate file types: Don't rely only on file extensions. Check MIME types.
  2. Limit file sizes: Prevent users from uploading extremely large files.
  3. Scan for malware: Consider using antivirus libraries.
  4. Use secure filenames: Generate random filenames to prevent filename exploits.
  5. Set proper permissions: Ensure your media directory has appropriate access restrictions.

Summary​

Django's media file handling system provides a robust way to manage user-uploaded content. We've covered:

  • Setting up media file configurations in Django settings
  • Creating models with FileField and ImageField
  • Building forms and views for file uploads
  • Processing and validating uploads
  • Creating a real-world photo gallery application
  • Production considerations and security best practices

With these tools, you can implement various types of file upload functionality in your Django applications.

Additional Resources​

Exercises​

  1. Create a document management system that allows users to upload, categorize, and search for PDF files.
  2. Build a profile system where users can upload and crop profile pictures.
  3. Implement a file validation system that checks uploaded files for viruses or malware.
  4. Create a multi-image uploader that allows users to upload multiple images at once with previews.
  5. Build a system that generates thumbnails of different sizes for uploaded images.


If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)