Chapter 11: Static and Media Files
11.2 Media File Handling
MEDIA_URL Configuration
Media files are user-uploaded files such as images, documents, videos, etc. Django provides dedicated configuration to handle media files.
Basic configuration:
# settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# Media file URL prefix
MEDIA_URL = '/media/'
# Media file storage directory
MEDIA_ROOT = BASE_DIR / "media"
# Ensure development server can serve media files
if DEBUG:
import mimetypes
mimetypes.add_type("image/svg+xml", ".svg", True)
mimetypes.add_type("image/svg+xml", ".svgz", True)Serve media files in development environment:
# 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('myapp.urls')),
]
# Serve media files in development environment
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)File Upload
Basic views and forms for handling file uploads:
# models.py
from django.db import models
class Document(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(upload_to='documents/%Y/%m/%d/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Image(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='images/%Y/%m/%d/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
# forms.py
from django import forms
from .models import Document, Image
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'file']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'file': forms.FileInput(attrs={'class': 'form-control'}),
}
class ImageForm(forms.ModelForm):
class Meta:
model = Image
fields = ['title', 'image']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'image': forms.FileInput(attrs={'class': 'form-control'}),
}
# views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import DocumentForm, ImageForm
def upload_document(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, 'File uploaded successfully!')
return redirect('document_list')
else:
form = DocumentForm()
return render(request, 'upload_document.html', {'form': form})
def upload_image(request):
if request.method == 'POST':
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.success(request, 'Image uploaded successfully!')
return redirect('image_list')
else:
form = ImageForm()
return render(request, 'upload_image.html', {'form': form})File upload templates:
<!-- upload_document.html -->
<div class="container">
<h2>Upload Document</h2>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="mb-3">
{{ form.title.label_tag }}
{{ form.title }}
{% if form.title.errors %}
<div class="text-danger">{{ form.title.errors }}</div>
{% endif %}
</div>
<div class="mb-3">
{{ form.file.label_tag }}
{{ form.file }}
{% if form.file.errors %}
<div class="text-danger">{{ form.file.errors }}</div>
{% endif %}
<div class="form-text">Supported file types: PDF, DOC, DOCX, TXT</div>
</div>
<button type="submit" class="btn btn-primary">Upload</button>
<a href="{% url 'document_list' %}" class="btn btn-secondary">Cancel</a>
</form>
</div>
<!-- document_list.html -->
<div class="container">
<h2>Document List</h2>
<div class="row">
{% for document in documents %}
<div class="col-md-4 mb-3">
<div class="card">
<div class="card-body">
<h5 class="card-title">{{ document.title }}</h5>
<p class="card-text">
Upload time: {{ document.uploaded_at|date:"Y-m-d H:i" }}
</p>
<a href="{{ document.file.url }}" class="btn btn-primary" target="_blank">
Download File
</a>
</div>
</div>
</div>
{% empty %}
<p>No documents.</p>
{% endfor %}
</div>
</div>Image Processing
Process image uploads using the Pillow library:
# Install Pillow
# pip install Pillow
# models.py
from django.db import models
from PIL import Image
import os
class Photo(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='photos/%Y/%m/%d/')
thumbnail = models.ImageField(upload_to='photos/thumbnails/%Y/%m/%d/', blank=True)
uploaded_at = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
# Save original image
super().save(*args, **kwargs)
# Generate thumbnail
if self.image and not self.thumbnail:
self.create_thumbnail()
def create_thumbnail(self):
# Open original image
img = Image.open(self.image.path)
# Resize image
max_size = (300, 300)
img.thumbnail(max_size, Image.Resampling.LANCZOS)
# Save thumbnail
thumb_name, thumb_extension = os.path.splitext(self.image.name)
thumb_extension = thumb_extension.lower()
thumb_path = thumb_name + '_thumb' + thumb_extension
if thumb_extension in ['.jpg', '.jpeg']:
img.save(self.thumbnail.path, 'JPEG', quality=85)
elif thumb_extension == '.png':
img.save(self.thumbnail.path, 'PNG', quality=85)
else:
img.save(self.thumbnail.path, 'JPEG', quality=85)
def __str__(self):
return self.title
# Custom image validation
from django.core.exceptions import ValidationError
def validate_image_size(value):
"""Validate image file size"""
filesize = value.size
if filesize > 5 * 1024 * 1024: # 5MB
raise ValidationError("Image size cannot exceed 5MB")
return value
def validate_image_extension(value):
"""Validate image file extension"""
valid_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']
ext = os.path.splitext(value.name)[1].lower()
if ext not in valid_extensions:
raise ValidationError(f'Unsupported file type. Supported types: {", ".join(valid_extensions)}')
# Use validators in model
class Photo(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(
upload_to='photos/%Y/%m/%d/',
validators=[validate_image_size, validate_image_extension]
)Display images in templates:
<!-- photo_list.html -->
<div class="container">
<h2>Photo Gallery</h2>
<div class="row">
{% for photo in photos %}
<div class="col-md-4 mb-4">
<div class="card">
{% if photo.thumbnail %}
<img src="{{ photo.thumbnail.url }}" class="card-img-top" alt="{{ photo.title }}">
{% else %}
<img src="{{ photo.image.url }}" class="card-img-top" alt="{{ photo.title }}">
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ photo.title }}</h5>
<p class="card-text">
Upload time: {{ photo.uploaded_at|date:"Y-m-d H:i" }}
</p>
<a href="{{ photo.image.url }}" class="btn btn-primary" target="_blank">
View Original
</a>
</div>
</div>
</div>
{% empty %}
<p>No photos.</p>
{% endfor %}
</div>
</div>File Storage Backends
Django supports multiple file storage backends, including local storage and cloud storage:
# settings.py
# Default local file storage
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# Using cloud storage (AWS S3 example)
# Install django-storages and boto3
# pip install django-storages boto3
# settings.py
INSTALLED_APPS = [
# ...
'storages',
]
# AWS S3 configuration
AWS_ACCESS_KEY_ID = 'your-access-key-id'
AWS_SECRET_ACCESS_KEY = 'your-secret-access-key'
AWS_STORAGE_BUCKET_NAME = 'your-bucket-name'
AWS_S3_REGION_NAME = 'us-east-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
# Static and media file storage
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
# Or configure static and media files separately
# STATICFILES_STORAGE = 'myapp.storage.StaticStorage'
# DEFAULT_FILE_STORAGE = 'myapp.storage.MediaStorage'
# Custom storage backend
# storage.py
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = 'static'
default_acl = 'public-read'
class MediaStorage(S3Boto3Storage):
location = 'media'
default_acl = 'public-read'
file_overwrite = False
# Alibaba Cloud OSS storage configuration
# Install django-oss-storage
# pip install django-oss-storage
# settings.py
DEFAULT_FILE_STORAGE = 'django_oss_storage.backends.OssMediaStorage'
STATICFILES_STORAGE = 'django_oss_storage.backends.OssStaticStorage'
OSS_ACCESS_KEY_ID = 'your-access-key-id'
OSS_ACCESS_KEY_SECRET = 'your-access-key-secret'
OSS_BUCKET_NAME = 'your-bucket-name'
OSS_ENDPOINT = 'http://oss-cn-hangzhou.aliyuncs.com'File access permission control:
# models.py
from django.db import models
from django.contrib.auth.models import User
class PrivateDocument(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(upload_to='private/%Y/%m/%d/')
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
# views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, Http404
from django.core.exceptions import PermissionDenied
from .models import PrivateDocument
@login_required
def download_private_document(request, document_id):
document = get_object_or_404(PrivateDocument, id=document_id)
# Check permissions
if document.owner != request.user and not request.user.is_superuser:
raise PermissionDenied("You don't have permission to download this file")
# Provide file download
response = HttpResponse(document.file, content_type='application/octet-stream')
response['Content-Disposition'] = f'attachment; filename="{document.file.name}"'
return responseWith these configurations and implementations, you can effectively handle media files in Django projects, including file uploads, image processing, and storage backend configuration.
Summary
Django media file handling provides a complete solution:
- ✅ MEDIA_URL and MEDIA_ROOT configuration manages media file access
- ✅ File upload forms and views handle user uploads
- ✅ Image processing features support thumbnail generation and validation
- ✅ Support for multiple storage backends, including cloud storage services
- ✅ File access permission control ensures security
Mastering media file handling skills can build feature-rich web applications.
Next Article
We will learn about Django middleware and signal systems.