This article looks at how to work with static and media files in a Django project, locally and in production.
Contents
Objectives
By the end of this article, you will be able to:
- Describe the three different types of files that you'll usually find in a Django project
- Explain the difference between static and media files
- Work with both static and media files locally and in production
Types of Files
Django is an opinionated, full-stack web application framework. It comes with many batteries that you can use to build a fully functional web application, including static and media file management.
Before we look at the how, let's start with some definitions.
What are static and media files?
First off, you'll typically find these three types of files in a Django project:
- Source code: These are your core Python modules and HTML files that make up every Django project, where you define your models, views, and templates.
- Static files: These are your CSS stylesheets, JavaScript files, fonts, and images. Since there's no processing involved, these files are very energy efficient since they can just be served up as is. They are also much easier to cache.
- Media file: These are files that a user uploads.
This article focuses on static and media files. Even though the names are different, both represent regular files. The significant difference is that static files are kept in version control and shipped with your source code files during deployment. On the other hand, media files are files that your end-users (internally and externally) upload or are dynamically created by your application (often as a side effect of some user action).
Why should you treat static and media files differently?
- You can't trust files uploaded by end-users, so media files need to be treated differently.
- You may need to perform processing on user uploaded, media files to be better served -- e.g., you could optimize uploaded images to support different devices.
- You don't want a user uploaded file to replace a static file accidentally.
Additional Notes:
- Static and media files are sometimes referred to as static and media assets.
- The Django admin comes with some static files, which are stored in version control on GitHub.
- Adding to the confusion between static and media files is that the Django documentation itself doesn't do a great job differentiating between the two.
Static Files
Django provides a powerful battery for working with static files, aptly named staticfiles.
If you're new to the staticfiles app, take a quick look at the How to manage static files (e.g., images, JavaScript, CSS) guide from the Django documentation.
Django's staticfiles app provides the following core components:
- Settings
- Management Commands
- Storage Classes
- Template Tags
Settings
There are a number of settings that you may need to configure, depending on your environment:
- STATIC_URL: URL where the user can access your static files from in the browser. The default is
/static/
, which means your files will be available athttp://127.0.0.1:8000/static/
in development mode -- e.g.,http://127.0.0.1:8000/static/css/main.css
. - STATIC_ROOT: The absolute path to the directory where your Django application will serve your static files from. When you run the collectstatic management command (more on this shortly), it will find all static files and copy them into this directory.
- STATICFILES_DIRS: By default, static files are stored at the app-level at
<APP_NAME>/static/
. The collectstatic command will look for static files in those directories. You can also tell Django to look for static files in additional locations withSTATICFILES_DIRS
. - STORAGES: It specifies a way to configure different storage backends for managing files. Each storage backend can be given an alias, and there are two special aliases:
default
for managing files (withFileSystemStorage
as the default storage engine) andstaticfiles
for managing static files (usingStaticFilesStorage
by default). - STATICFILES_FINDERS: This setting defines the file finder backends to be used to automatically find static files. By default, the
FileSystemFinder
andAppDirectoriesFinder
finders are used:FileSystemFinder
- uses theSTATICFILES_DIRS
setting to find files.AppDirectoriesFinder
- looks for files in a "static" folder in each Django app within the project.
Management Commands
The staticfiles apps provides the following management commands:
collectstatic
is a management command that collects static files from the various locations -- i.e.,<APP_NAME>/static/
and the directories found in theSTATICFILES_DIRS
setting -- and copies them to theSTATIC_ROOT
directory.findstatic
is a really helpful command to use when debugging so you can see exactly where a specific file comes from.runserver
starts a lightweight, development server to run your Django application in development.
Notes:
- Don't put any static files in the
STATIC_ROOT
directory. That's where the static files get copied to automatically after you runcollectstatic
. Instead, always put them in the directories associated with theSTATICFILES_DIRS
setting or<APP_NAME>/static/
. - Do not use the development server in production. Use a production-grade WSGI application server instead. More on this shortly.
Quick example of the
findstatic
command:Say you have two Django apps,
app1
andapp2
. Each app has a folder named "static", and within each of those folders, a file called app.css. Relevant settings from settings.py:STATIC_ROOT = 'staticfiles' INSTALLED_APPS = [ ... 'app1', 'app2', ]
When
python manage.py collectstatic
is run, the "staticfiles" directory will be created and the appropriate static files will be copied into it:$ ls staticfiles/ admin app.css
There's only one app.css file because when multiple files with the same name are present, the staticfiles finder will use the first found file. To see which file is copied over, you can use the
findstatic
command:$ python manage.py findstatic app.css Found 'app.css' here: /app1/static/app.css /app2/static/app.css
Since only the first encountered file is collected, to check the source of the app.css that was copied over to the "staticfiles" directory, run:
$ python manage.py findstatic app.css --first Found 'app.css' here: /app1/static/app.css
Storage Classes
When the collectstatic
command is run, Django uses storage classes to determine how the static files are stored and accessed. Again, this is configured via the STORAGES setting.
The default storage class is StaticFilesStorage. Behind the scenes, StaticFilesStorage
uses the FileSystemStorage class to store files on the local filesystem.
You may want to deviate from the default in production. For example, django-storages provides a few custom storage classes for different cloud/CDN providers. You can also write your own storage class using the file storage API. Review Serving static files from a cloud service or CDN for more on this.
Storage classes can be used to perform post processing tasks like minification.
Template Tags
To load static files in your template files, you need to:
- Add
{% load static %}
to the top of the template file - Then, for each file you'd like to link, add the
{% static %}
template tag
For example:
{% load static %}
<link rel="stylesheet" href="{% static 'base.css' %}">
Together, these tags generate a complete URL -- e.g, /static/base.css
-- based on the static files configuration in the settings.py file.
You should always load static files in this manner rather than hard coding the URL directly so that you can change your static file configuration and point to a different STATIC_URL
without having to manually update each template.
For more on these template tags, review the static section from Built-in template tags and filters.
Static Files in Development Mode
During development, as long as you have DEBUG
set to TRUE
and you're using the staticfiles app, you can serve up static files using Django's development server. You don't even need to run the collecstatic
command.
Typical development config:
# settings.py
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'static',]
STORAGES = {
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
},
}
Static Files in Production
Handling static files in production isn't quite as easy as your development environment since you'll be using either a WSGI (like Gunicorn) or ASGI (like Uvicorn) compatible web application server, which are used to serve up the dynamic content -- i.e., your Django source code files.
There are a number of different ways to handle static files in production, but the two most popular options are:
- Use a web server like Nginx to route traffic destined for your static files directly to the static root (configured via
STATIC_ROOT
) - Use WhiteNoise to serve up static files directly from the WSGI or ASGI web application server
Regardless of the option, you'll probably want to leverage a CDN.
For more on these options, review How to deploy static files.
Nginx
Sample Nginx config:
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /static/ {
alias /home/app/web/staticfiles/;
}
}
In short, when a request is sent to /static/
-- e.g, /static/base.css
-- Nginx will attempt to serve the file from the "/home/app/web/staticfiles/" folder.
Curious how the above Nginx config works? Check out the Dockerizing Django with Postgres, Gunicorn, and Nginx tutorial.
Additional resources:
- Prefer to store your static files on Amazon S3? Check out Storing Django Static and Media Files on Amazon S3.
- Prefer to store your static files on DigitalOcean Spaces? Check out Storing Django Static and Media Files on DigitalOcean Spaces.
WhiteNoise
You can use WhiteNoise to serve static files from a WSGI or ASGI web application server.
The most basic set up is simple. After you install the package, add WhiteNoise to the MIDDLEWARE
list above all other middleware apart from django.middleware.security.SecurityMiddleware
:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # <---- WhiteNoise!
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Then, for compression and caching support, update STORAGES
like so:
STORAGES = {
'staticfiles': {
'BACKEND': 'whitenoise.storage.CompressedManifestStaticFilesStorage',
}
}
That's it! Turn off debug mode, run the collectstatic
command, and then run your WSGI or ASGI web application server.
Refer to the Using WhiteNoise with Django guide for more on configuring WhiteNoise to work with Django.
Media Files
Again, media files are files that your end-users (internally and externally) upload or are dynamically created by your application (often as a side effect of some user action). They are not typically kept in version control.
Almost always, the files associated with the FileField or ImageField model fields should be treated as media files.
Just like with static files, the handling of media files is configured in the settings.py file.
Essential configuration settings for handling media files:
- MEDIA_URL: Similar to the
STATIC_URL
, this is the URL where users can access media files. - MEDIA_ROOT: The absolute path to the directory where your Django application will serve your media files from.
Refer to the File uploads section from Settings for additional configuration settings.
Media Files in Development Mode
Typical development config:
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'uploads'
Unfortunately, the Django development server doesn't serve media files by default. Fortunately, there's a very simple workaround: You can add the media root as a static path to the ROOT_URLCONF
in your project-level URLs.
Example:
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),
# ... the rest of your URLconf goes here ...
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Media Files in Production
When it comes to handling media files in production, you have less options than you do for static files since you can't use WhiteNoise for serving media files. Thus, you'll typically want to use Nginx along with django-storages to store media files outside the local file system where your application is running in production.
Sample Nginx config:
upstream hello_django {
server web:8000;
}
server {
listen 80;
location / {
proxy_pass http://hello_django;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /media/ {
alias /home/app/web/mediafiles/;
}
}
So, when a request is sent to /media/
-- e.g, /media/upload.png
-- Nginx will attempt to serve the file from the "/home/app/web/mediafiles/" folder.
Curious how the above Nginx config works? Check out the Dockerizing Django with Postgres, Gunicorn, and Nginx tutorial.
Additional resources:
- Prefer to store your media files on Amazon S3? Check out Storing Django Static and Media Files on Amazon S3.
- Prefer to store your media files on DigitalOcean Spaces? Check out Storing Django Static and Media Files on DigitalOcean Spaces.
Conclusion
Static and media files are different and must be treated differently for security purposes.
In this article, you saw examples of how to serve static and media files in development and production. In addition, the article also covered:
- The different settings for both types of files
- How Django handles them with minimal configuration
You can find a simple Django project with examples for serving static files in development and production and media files in development here.
This article only walks you through handling static and media files in Django. It does not discuss pre/post-processing for the static files such as minifying and bundling. For such tasks, you have to set up complex build processes with tools like Rollup, Parcel, or webpack.