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 [email protected]. I’d greatly appreciate your feedback! :)