Skip to content

Static & Media Files#

Complete guide to handling static files and media uploads in Django.

📁 Static Files#

What are Static Files?#

Static files are files that don't change per request: - CSS files - JavaScript files - Images (logos, icons) - Fonts - Other assets

Development Setup#

1. Create Static Directories#

myproject/
├── myapp/
│   └── static/
│       └── myapp/
│           ├── css/
│           │   └── style.css
│           ├── js/
│           │   └── main.js
│           └── images/
│               └── logo.png
└── static/  # Project-wide static files
    └── css/
        └── global.css

2. Settings Configuration#

# myproject/settings.py

# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'  # For production

# Additional static file locations
STATICFILES_DIRS = [
    BASE_DIR / 'static',  # Project-wide static files
]

# Static files finders
STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

3. Include Static Files in Templates#

<!-- Load static template tag -->
{% load static %}

<!-- CSS -->
<link rel="stylesheet" href="{% static 'myapp/css/style.css' %}">
<link rel="stylesheet" href="{% static 'css/global.css' %}">

<!-- JavaScript -->
<script src="{% static 'myapp/js/main.js' %}"></script>

<!-- Images -->
<img src="{% static 'myapp/images/logo.png' %}" alt="Logo">

4. URL Configuration (Development)#

# myproject/urls.py
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... your URL patterns
]

# Serve static files in development
if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

Production Setup#

Collect Static Files#

# Collect all static files to STATIC_ROOT
python manage.py collectstatic

# This copies all static files from:
# - STATICFILES_DIRS
# - Each app's static/ directory
# To: STATIC_ROOT
# Install
pip install whitenoise
# myproject/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',  # Add this
    # ... other middleware
]

# WhiteNoise configuration
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

Serve with Web Server (Alternative)#

# nginx.conf
server {
    location /static/ {
        alias /path/to/staticfiles/;
    }
}

📸 Media Files#

What are Media Files?#

Media files are user-uploaded files: - User profile pictures - Document uploads - Images uploaded through forms - Any file uploaded by users

Development Setup#

1. Settings Configuration#

# myproject/settings.py

# Media files (user uploads)
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

2. URL Configuration (Development)#

# myproject/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)

3. Model Configuration#

# myapp/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    image = models.ImageField(upload_to='posts/')  # Uploads to media/posts/
    document = models.FileField(upload_to='documents/')  # Uploads to media/documents/

    # Date-based upload path
    photo = models.ImageField(upload_to='photos/%Y/%m/%d/')
    # Uploads to: media/photos/2024/01/15/filename.jpg

4. Form Configuration#

# myapp/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'image', 'document']
        widgets = {
            'image': forms.FileInput(attrs={'accept': 'image/*'}),
            'document': forms.FileInput(attrs={'accept': '.pdf,.doc,.docx'}),
        }

5. Template Configuration#

<!-- Display uploaded image -->
{% if post.image %}
    <img src="{{ post.image.url }}" alt="{{ post.title }}">
{% endif %}

<!-- Display uploaded file -->
{% if post.document %}
    <a href="{{ post.document.url }}">Download Document</a>
{% endif %}

<!-- Form with file upload -->
<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Upload</button>
</form>

Important: Always use enctype="multipart/form-data" in forms with file uploads!

Production Setup#

Serve with Web Server#

# nginx.conf
server {
    # Static files
    location /static/ {
        alias /path/to/staticfiles/;
    }

    # Media files
    location /media/ {
        alias /path/to/media/;
    }
}
# Install
pip install django-storages boto3  # For AWS S3
# Or
pip install django-storages  # For other providers
# myproject/settings.py
# AWS S3 Configuration
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3StaticStorage'

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_S3_REGION_NAME = 'us-east-1'
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'

🎯 File Upload Handling#

View with File Upload#

# myapp/views.py
from django.shortcuts import render, redirect
from .forms import PostForm
from .models import Post

def post_create(request):
    if request.method == 'POST':
        form = PostForm(request.POST, request.FILES)  # Include request.FILES
        if form.is_valid():
            post = form.save()
            return redirect('myapp:post_detail', pk=post.pk)
    else:
        form = PostForm()

    return render(request, 'myapp/post_form.html', {'form': form})

Manual File Handling#

# myapp/views.py
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile

def upload_file(request):
    if request.method == 'POST' and request.FILES:
        uploaded_file = request.FILES['file']

        # Save file
        file_path = default_storage.save(
            f'uploads/{uploaded_file.name}',
            ContentFile(uploaded_file.read())
        )

        # Get file URL
        file_url = default_storage.url(file_path)

        return render(request, 'myapp/success.html', {'file_url': file_url})

    return render(request, 'myapp/upload.html')

📋 File Field Options#

upload_to Options#

# Simple path
image = models.ImageField(upload_to='images/')

# Date-based path
image = models.ImageField(upload_to='images/%Y/%m/%d/')

# Callable function
def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return f'user_{instance.user.id}/{filename}'

image = models.ImageField(upload_to=user_directory_path)

# Model method
class Post(models.Model):
    def get_upload_path(self, filename):
        return f'posts/{self.slug}/{filename}'

    image = models.ImageField(upload_to=get_upload_path)

File Validation#

# myapp/models.py
from django.core.exceptions import ValidationError

def validate_image_size(value):
    # Limit file size (5MB)
    if value.size > 5 * 1024 * 1024:
        raise ValidationError('Image size cannot exceed 5MB.')

def validate_image_extension(value):
    # Allow only specific extensions
    allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif']
    if not any(value.name.lower().endswith(ext) for ext in allowed_extensions):
        raise ValidationError('Invalid image format.')

class Post(models.Model):
    image = models.ImageField(
        upload_to='posts/',
        validators=[validate_image_size, validate_image_extension]
    )

🗑️ Delete Files#

Delete File When Model Deleted#

# myapp/models.py
from django.db.models.signals import pre_delete
from django.dispatch import receiver

@receiver(pre_delete, sender=Post)
def delete_post_files(sender, instance, **kwargs):
    # Delete associated files
    if instance.image:
        instance.image.delete(save=False)
    if instance.document:
        instance.document.delete(save=False)

Manual File Deletion#

from django.core.files.storage import default_storage

# Delete file
default_storage.delete('path/to/file.jpg')

📁 Directory Structure#

myproject/
├── myapp/
│   └── static/
│       └── myapp/
│           ├── css/
│           ├── js/
│           └── images/
├── static/          # Project-wide static files
│   ├── css/
│   ├── js/
│   └── images/
├── staticfiles/     # Collected static files (production)
└── media/           # User uploads
    ├── posts/
    ├── documents/
    └── photos/

⚙️ Settings Summary#

# myproject/settings.py

# Static Files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

# Media Files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

# Static files finders (default)
STATICFILES_FINDERS = [
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
]

✅ Best Practices#

  1. Use {% load static %} - Always load static tag in templates
  2. Organize by app - Keep static files in app/static/app/
  3. Use collectstatic - Before deploying to production
  4. Use WhiteNoise - For serving static files in production
  5. Validate uploads - Check file size and type
  6. Use upload_to - Organize media files properly
  7. Delete old files - Clean up when models are deleted
  8. Use cloud storage - For production media files

✅ Next Steps#


Pro Tip: Always use enctype="multipart/form-data" in forms with file uploads. Use request.FILES in views to access uploaded files. In production, serve media files with nginx or use cloud storage!