File And Image Fields
Store user uploads with FileField and ImageField. Configure upload_to, storage backends, Pillow, validations, and clean up files on delete.
1. Introduction
Django provides two fields for handling user uploads: FileField for any file type and ImageField specifically for images. Both fields store the file path in the database — not the file itself. The actual file is saved to the folder you define in MEDIA_ROOT.
This guide covers how to set up both fields, configure upload paths, install Pillow for image support, validate uploads, and handle file cleanup.
- Your
MEDIA_URLandMEDIA_ROOTmust already be configured in settings. If not, see Static and Media Configuration. - Your
.venvmust be active and Django 5.2 installed.
2. FileField
Use FileField to allow users to upload any type of file — PDFs, documents, spreadsheets, zip files. The field stores the relative file path in the database.
# pages/models.py
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)
def __str__(self):
return self.title
The upload_to argument tells Django where inside MEDIA_ROOT to save the file. In this example, uploaded files go to media/documents/. Django creates this folder automatically if it does not exist.
3. ImageField
ImageField works exactly like FileField but adds validation to confirm the uploaded file is a valid image. It requires the Pillow library to be installed.
class Profile(models.Model):
user = models.OneToOneField('auth.User', on_delete=models.CASCADE)
photo = models.ImageField(upload_to='profiles/', blank=True)
bio = models.TextField(blank=True)
def __str__(self):
return f'Profile of {self.user.username}'
Install Pillow inside your active .venv:
pip install Pillow
pip freeze > requirements.txt
ImageField. Install it before running makemigrations.
4. Configuring upload_to
upload_to controls where files are saved inside MEDIA_ROOT. You can use a static string, a path with date formatting, or a callable function.
Static folder
photo = models.ImageField(upload_to='photos/')
# Saves to: media/photos/filename.jpg
Date-based subfolders
photo = models.ImageField(upload_to='photos/%Y/%m/')
# Saves to: media/photos/2026/05/filename.jpg
Callable — full control over the path
import os
import uuid
def upload_profile_photo(instance, filename):
ext = filename.split('.')[-1]
filename = f'{uuid.uuid4()}.{ext}'
return os.path.join('profiles', str(instance.user.id), filename)
class Profile(models.Model):
photo = models.ImageField(upload_to=upload_profile_photo, blank=True)
The callable approach is the most flexible. It lets you rename files, organize them by user ID, and avoid filename conflicts by using a UUID. instance is the model instance being saved and filename is the original file name from the upload.
5. Accessing files in templates
File and image fields provide a .url property that builds the full URL to the file using MEDIA_URL. Always use .url in templates — never build the path manually.
<img src="/static/images/default-avatar.png" alt="Default avatar">
Always check if the field has a value before calling .url. Calling .url on an empty field raises a ValueError.
6. Validating uploads
Django does not restrict file types or sizes by default. You need to add validation yourself. The best place for this is a custom validator function:
# pages/validators.py
from django.core.exceptions import ValidationError
def validate_file_size(value):
limit = 5 * 1024 * 1024 # 5 MB
if value.size > limit:
raise ValidationError('File size must be under 5 MB.')
def validate_image_extension(value):
allowed = ['.jpg', '.jpeg', '.png', '.webp']
ext = value.name.lower().rsplit('.', 1)[-1]
if f'.{ext}' not in allowed:
raise ValidationError('Only JPG, PNG, and WebP images are allowed.')
Apply the validators to the field:
from .validators import validate_file_size, validate_image_extension
class Profile(models.Model):
photo = models.ImageField(
upload_to='profiles/',
blank=True,
validators=[validate_file_size, validate_image_extension]
)
full_clean() or submit a ModelForm. They do not run automatically when you call save() directly. For form-based uploads this is fine — forms always run validation before saving.
7. Cleaning up files on delete
When you delete a model instance, Django removes the database row but does not delete the associated file from disk. Over time this leaves orphaned files in your media folder.
Use a post_delete signal to clean up files automatically:
# pages/models.py
from django.db import models
from django.db.models.signals import post_delete
from django.dispatch import receiver
class Profile(models.Model):
photo = models.ImageField(upload_to='profiles/', blank=True)
@receiver(post_delete, sender=Profile)
def delete_profile_photo(sender, instance, **kwargs):
if instance.photo:
instance.photo.delete(save=False)
save=False tells Django to delete the file from storage without trying to save the model instance again — which would fail since the record has already been deleted.
8. Next steps
File and image uploads are now covered. The next step is choices and enums — a clean way to restrict a field to a fixed set of values and keep that logic inside the model.