Django File Uploads
Handling file and image uploads in Django
📁 What are File Uploads?
Django makes it easy to handle file uploads from users. You can accept images, documents, videos, and any file type. Django handles validation, storage, and security automatically with simple configuration.
# Simple file upload model
from django.db import models
class Document(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(upload_to='documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
File Upload Types
Images
Upload and validate image files
class Profile(models.Model):
avatar = models.ImageField(
upload_to='avatars/'
)
Documents
Handle PDF, Word, Excel files
class Report(models.Model):
document = models.FileField(
upload_to='reports/'
)
Media
Upload audio and video files
class Song(models.Model):
audio = models.FileField(
upload_to='music/'
)
Archives
Handle ZIP and compressed files
class Backup(models.Model):
archive = models.FileField(
upload_to='backups/'
)
🔹 Basic File Upload Setup
Configure Django to handle file uploads by setting up media files in your settings. This tells Django where to store uploaded files and how to serve them during development.
# settings.py
import os
# Media files configuration
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# Your URL patterns
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
🔹 Creating Upload Model
Define models with FileField or ImageField to store uploaded files. The upload_to parameter organizes files into folders automatically.
# models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField(upload_to='avatars/', blank=True, null=True)
bio = models.TextField(max_length=500, blank=True)
def __str__(self):
return f"{self.user.username}'s profile"
class Document(models.Model):
title = models.CharField(max_length=200)
description = models.TextField(blank=True)
file = models.FileField(upload_to='documents/%Y/%m/%d/')
uploaded_by = models.ForeignKey(User, on_delete=models.CASCADE)
uploaded_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
File Storage Structure:
media/avatars/user_photo.jpg
media/documents/2024/01/15/report.pdf
🔹 Creating Upload Form
Use Django forms to handle file uploads with validation. ModelForm automatically creates form fields based on your model definition.
# forms.py
from django import forms
from .models import Document, UserProfile
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
fields = ['title', 'description', 'file']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'file': forms.FileInput(attrs={'class': 'form-control'}),
}
class ProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ['avatar', 'bio']
🔹 Handling Upload in Views
Process file uploads in views using request.FILES. Always use POST method and include enctype in your form for file uploads to work properly.
# views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .forms import DocumentForm
from .models import Document
@login_required
def upload_document(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.uploaded_by = request.user
document.save()
return redirect('document_list')
else:
form = DocumentForm()
return render(request, 'upload.html', {'form': form})
def document_list(request):
documents = Document.objects.all().order_by('-uploaded_at')
return render(request, 'documents.html', {'documents': documents})
🔹 Upload Template
Create an HTML form with enctype="multipart/form-data" to enable file uploads. This encoding type is required for sending files to the server.
<!-- upload.html -->
<h2>Upload Document</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<label>Title:</label>
{{ form.title }}
</div>
<div class="form-group">
<label>Description:</label>
{{ form.description }}
</div>
<div class="form-group">
<label>File:</label>
{{ form.file }}
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
<!-- Display uploaded files -->
<h3>Uploaded Documents</h3>
{% for doc in documents %}
<div class="document">
<h4>{{ doc.title }}</h4>
<p>{{ doc.description }}</p>
<a href="{{ doc.file.url }}" download>Download</a>
<small>Uploaded: {{ doc.uploaded_at }}</small>
</div>
{% endfor %}
🔹 File Validation
Validate file size, type, and other properties to ensure security and prevent malicious uploads.
# validators.py
from django.core.exceptions import ValidationError
def validate_file_size(file):
max_size_mb = 5
if file.size > max_size_mb * 1024 * 1024:
raise ValidationError(f'File size cannot exceed {max_size_mb}MB')
def validate_image_extension(file):
valid_extensions = ['.jpg', '.jpeg', '.png', '.gif']
ext = file.name.lower().split('.')[-1]
if f'.{ext}' not in valid_extensions:
raise ValidationError('Only JPG, PNG, and GIF images are allowed')
# models.py
class Photo(models.Model):
image = models.ImageField(
upload_to='photos/',
validators=[validate_file_size, validate_image_extension]
)
Validation Errors:
❌ File size cannot exceed 5MB
❌ Only JPG, PNG, and GIF images are allowed
🔹 Image Processing Example
Automatically resize and optimize uploaded images using Pillow library to save storage space and improve performance.
# models.py
from django.db import models
from PIL import Image
class Photo(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='photos/')
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
# Resize image
img = Image.open(self.image.path)
if img.height > 800 or img.width > 800:
output_size = (800, 800)
img.thumbnail(output_size)
img.save(self.image.path)