Django's storage system provides an abstraction layer for file operations, allowing you to switch between different storage backends without changing your application code. This flexibility is essential for scalable applications that need to handle files efficiently.
Django's default storage backend saves files to the local file system:
# settings.py
import os
# Media files configuration
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Static files configuration
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
from django.core.files.storage import FileSystemStorage
# Custom file system storage
custom_storage = FileSystemStorage(
location='/path/to/custom/directory',
base_url='/custom-media/',
file_permissions_mode=0o644,
directory_permissions_mode=0o755
)
# Using in models
class Document(models.Model):
title = models.CharField(max_length=200)
file = models.FileField(
upload_to='documents/',
storage=custom_storage
)
All storage backends implement the same interface:
from django.core.files.storage import Storage
class CustomStorage(Storage):
"""Custom storage implementation"""
def _open(self, name, mode='rb'):
"""Open file and return file object"""
pass
def _save(self, name, content):
"""Save file and return the name"""
pass
def delete(self, name):
"""Delete file"""
pass
def exists(self, name):
"""Check if file exists"""
pass
def listdir(self, path):
"""List directory contents"""
pass
def size(self, name):
"""Return file size"""
pass
def url(self, name):
"""Return URL for file"""
pass
def get_accessed_time(self, name):
"""Return last accessed time"""
pass
def get_created_time(self, name):
"""Return creation time"""
pass
def get_modified_time(self, name):
"""Return last modified time"""
pass
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
def storage_operations():
"""Demonstrate storage operations"""
# Save file
content = ContentFile(b"Hello, World!")
filename = default_storage.save('test.txt', content)
# Check if file exists
if default_storage.exists(filename):
print(f"File {filename} exists")
# Get file size
size = default_storage.size(filename)
print(f"File size: {size} bytes")
# Get file URL
url = default_storage.url(filename)
print(f"File URL: {url}")
# Open and read file
with default_storage.open(filename, 'rb') as f:
content = f.read()
print(f"Content: {content}")
# Delete file
default_storage.delete(filename)
# settings.py
from django.core.files.storage import FileSystemStorage
# Different storage configurations
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
"OPTIONS": {
"location": os.path.join(BASE_DIR, "media"),
"base_url": "/media/",
},
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
"OPTIONS": {
"location": os.path.join(BASE_DIR, "staticfiles"),
},
},
"documents": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
"OPTIONS": {
"location": os.path.join(BASE_DIR, "documents"),
"base_url": "/documents/",
},
},
}
# Custom storage instances
document_storage = FileSystemStorage(
location=os.path.join(BASE_DIR, 'documents'),
base_url='/documents/'
)
secure_storage = FileSystemStorage(
location=os.path.join(BASE_DIR, 'secure'),
base_url=None # No direct URL access
)
from django.db import models
from myapp.storage import document_storage, secure_storage
class Document(models.Model):
title = models.CharField(max_length=200)
# Public document
public_file = models.FileField(
upload_to='public/',
storage=document_storage
)
# Secure document (no direct URL)
secure_file = models.FileField(
upload_to='secure/',
storage=secure_storage
)
created_at = models.DateTimeField(auto_now_add=True)
from django.core.files.storage import Storage
from django.core.files.base import ContentFile
from django.utils.deconstruct import deconstructible
import io
@deconstructible
class DatabaseStorage(Storage):
"""Store files in database"""
def _open(self, name, mode='rb'):
"""Open file from database"""
try:
from myapp.models import StoredFile
stored_file = StoredFile.objects.get(name=name)
return ContentFile(stored_file.content)
except StoredFile.DoesNotExist:
raise FileNotFoundError(f"File {name} not found")
def _save(self, name, content):
"""Save file to database"""
from myapp.models import StoredFile
# Read content
content.seek(0)
file_content = content.read()
# Save to database
stored_file, created = StoredFile.objects.get_or_create(
name=name,
defaults={'content': file_content}
)
if not created:
stored_file.content = file_content
stored_file.save()
return name
def delete(self, name):
"""Delete file from database"""
from myapp.models import StoredFile
try:
stored_file = StoredFile.objects.get(name=name)
stored_file.delete()
except StoredFile.DoesNotExist:
pass
def exists(self, name):
"""Check if file exists in database"""
from myapp.models import StoredFile
return StoredFile.objects.filter(name=name).exists()
def size(self, name):
"""Get file size"""
from myapp.models import StoredFile
try:
stored_file = StoredFile.objects.get(name=name)
return len(stored_file.content)
except StoredFile.DoesNotExist:
return 0
def url(self, name):
"""Return URL for file"""
from django.urls import reverse
return reverse('serve_db_file', args=[name])
# Model for database storage
class StoredFile(models.Model):
name = models.CharField(max_length=255, unique=True)
content = models.BinaryField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
from cryptography.fernet import Fernet
from django.conf import settings
from django.core.files.storage import FileSystemStorage
import os
@deconstructible
class EncryptedFileSystemStorage(FileSystemStorage):
"""Encrypted file system storage"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Get encryption key from settings
key = getattr(settings, 'FILE_ENCRYPTION_KEY', None)
if not key:
key = Fernet.generate_key()
self.cipher = Fernet(key)
def _save(self, name, content):
"""Save encrypted file"""
# Read and encrypt content
content.seek(0)
original_content = content.read()
encrypted_content = self.cipher.encrypt(original_content)
# Create new content file
from django.core.files.base import ContentFile
encrypted_file = ContentFile(encrypted_content)
# Save encrypted file
return super()._save(name, encrypted_file)
def _open(self, name, mode='rb'):
"""Open and decrypt file"""
# Open encrypted file
encrypted_file = super()._open(name, mode)
# Read and decrypt content
encrypted_content = encrypted_file.read()
decrypted_content = self.cipher.decrypt(encrypted_content)
# Return decrypted content
from django.core.files.base import ContentFile
return ContentFile(decrypted_content)
# settings.py
# Default storage settings
DEFAULT_FILE_STORAGE = 'myapp.storage.CustomStorage'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage'
# File upload settings
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # 2.5 MB
FILE_UPLOAD_TEMP_DIR = '/tmp'
FILE_UPLOAD_PERMISSIONS = 0o644
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755
# Media settings
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Custom storage settings
CUSTOM_STORAGE_OPTIONS = {
'location': '/path/to/files',
'base_url': '/files/',
'file_permissions_mode': 0o644,
'directory_permissions_mode': 0o755,
}
# settings/base.py
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
}
# settings/production.py
STORAGES = {
"default": {
"BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
"OPTIONS": {
"access_key": os.environ.get("AWS_ACCESS_KEY_ID"),
"secret_key": os.environ.get("AWS_SECRET_ACCESS_KEY"),
"bucket_name": os.environ.get("AWS_STORAGE_BUCKET_NAME"),
"region_name": os.environ.get("AWS_S3_REGION_NAME"),
},
},
}
# settings/development.py
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
"OPTIONS": {
"location": os.path.join(BASE_DIR, "dev_media"),
"base_url": "/dev_media/",
},
},
}
from django.core.files.storage import get_storage_class
from django.conf import settings
def get_storage(storage_name='default'):
"""Get storage instance by name"""
storage_config = settings.STORAGES.get(storage_name)
if not storage_config:
raise ValueError(f"Storage '{storage_name}' not configured")
storage_class = get_storage_class(storage_config['BACKEND'])
return storage_class(**storage_config.get('OPTIONS', {}))
def copy_file_between_storages(source_storage, dest_storage, filename):
"""Copy file between different storage backends"""
# Open file from source storage
with source_storage.open(filename, 'rb') as source_file:
content = source_file.read()
# Save to destination storage
from django.core.files.base import ContentFile
content_file = ContentFile(content)
dest_storage.save(filename, content_file)
def migrate_files_to_new_storage(old_storage, new_storage, file_list):
"""Migrate files to new storage backend"""
for filename in file_list:
if old_storage.exists(filename):
try:
copy_file_between_storages(old_storage, new_storage, filename)
print(f"Migrated: {filename}")
except Exception as e:
print(f"Failed to migrate {filename}: {e}")
from django.core.cache import cache
from django.core.files.storage import Storage
class CachedStorage(Storage):
"""Storage with caching layer"""
def __init__(self, base_storage):
self.base_storage = base_storage
self.cache_timeout = 3600 # 1 hour
def exists(self, name):
"""Cache file existence checks"""
cache_key = f"file_exists:{name}"
result = cache.get(cache_key)
if result is None:
result = self.base_storage.exists(name)
cache.set(cache_key, result, self.cache_timeout)
return result
def size(self, name):
"""Cache file size"""
cache_key = f"file_size:{name}"
result = cache.get(cache_key)
if result is None:
result = self.base_storage.size(name)
cache.set(cache_key, result, self.cache_timeout)
return result
def url(self, name):
"""Cache file URLs"""
cache_key = f"file_url:{name}"
result = cache.get(cache_key)
if result is None:
result = self.base_storage.url(name)
cache.set(cache_key, result, self.cache_timeout)
return result
Now that you understand storage backends, let's explore how to integrate with cloud storage providers like AWS S3, Google Cloud Storage, and Azure Blob Storage.
The File Object
Django's file handling system centers around the File object, which provides a consistent interface for working with files regardless of their storage backend. Understanding the File object is crucial for advanced file manipulation and custom storage implementations.
Using Cloud Storage Providers
Cloud storage providers offer scalable, reliable, and cost-effective solutions for handling files in production Django applications. This chapter covers integration with major cloud storage services and best practices for cloud-based file management.