Static Files Management
Static files management in this project is handled by the django.contrib.staticfiles application. It provides a unified interface for discovering, serving, and collecting assets like CSS, JavaScript, and images across multiple applications and custom directories.
Discovery with Finders
The core of the static files system is the "finder" mechanism. Finders are responsible for locating static assets based on project settings. The two primary finders used are FileSystemFinder and AppDirectoriesFinder, both located in django/contrib/staticfiles/finders.py.
FileSystemFinder
The FileSystemFinder searches for files in the directories defined in the STATICFILES_DIRS setting. It supports optional prefixes, allowing you to map a specific directory to a sub-path in the static URL space.
# django/contrib/staticfiles/finders.py
class FileSystemFinder(BaseFinder):
def __init__(self, app_names=None, *args, **kwargs):
self.locations = []
self.storages = {}
for root in settings.STATICFILES_DIRS:
if isinstance(root, (list, tuple)):
prefix, root = root
else:
prefix = ""
if (prefix, root) not in self.locations:
self.locations.append((prefix, root))
# ... initializes storage for each location
AppDirectoriesFinder
The AppDirectoriesFinder automatically looks for a static/ subdirectory within each application listed in INSTALLED_APPS. This allows apps to ship their own assets without requiring manual configuration in STATICFILES_DIRS.
Development Serving
During development, static files are served dynamically to avoid the need for a collection step every time a file changes. This is implemented via the StaticFilesHandler in django/contrib/staticfiles/handlers.py.
The StaticFilesHandler is a WSGI middleware that intercepts requests starting with the STATIC_URL prefix. If a request matches, it uses the finders to locate the file and serves it using Django's internal file-serving views.
# django/contrib/staticfiles/handlers.py
class StaticFilesHandler(StaticFilesHandlerMixin, WSGIHandler):
def __call__(self, environ, start_response):
if not self._should_handle(get_path_info(environ)):
return self.application(environ, start_response)
return super().__call__(environ, start_response)
This handler is automatically integrated into the runserver command when DEBUG is enabled, ensuring a seamless development experience.
Production Collection
For production environments, assets must be gathered into a single location (defined by STATIC_ROOT) so they can be served efficiently by a web server like Nginx or Apache. This is performed by the collectstatic management command.
The Collection Process
The Command class in django/contrib/staticfiles/management/commands/collectstatic.py orchestrates the collection. The collect() method iterates through all registered finders, retrieves every file they find, and copies (or symlinks) them to the destination storage.
# django/contrib/staticfiles/management/commands/collectstatic.py
def collect(self):
# ... setup handler (copy_file or link_file)
found_files = {}
for finder in get_finders():
for path, storage in finder.list(self.ignore_patterns):
# Prefix the relative path if the source storage contains it
if getattr(storage, "prefix", None):
prefixed_path = os.path.join(storage.prefix, path)
else:
prefixed_path = path
if prefixed_path not in found_files:
found_files[prefixed_path] = (storage, path)
handler(path, prefixed_path, storage)
else:
# Logs a warning if a duplicate file is found
self.log("Found another file... It will be ignored", level=2)
Conflict Resolution
If multiple finders find a file with the same relative path, collectstatic only collects the first one encountered. The order of finders in STATICFILES_FINDERS and the order of apps in INSTALLED_APPS determines which file takes precedence.
Storage and Optimization
The StaticFilesStorage class in django/contrib/staticfiles/storage.py serves as the default storage backend for collected files. It is a specialized version of FileSystemStorage that defaults its location to STATIC_ROOT.
Cache Busting with Manifests
To support long-term caching, the project includes ManifestStaticFilesStorage. This storage backend performs "post-processing" during the collectstatic run:
- It appends a content-based hash to filenames (e.g.,
styles.cssbecomesstyles.55e7c10.css). - It generates a
staticfiles.jsonmanifest file that maps original paths to their hashed versions. - It updates references within CSS files to point to the new hashed filenames of other assets.
Configuration and System Checks
The StaticFilesConfig class (the AppConfig for staticfiles) ensures the system is configured correctly by registering system checks during startup.
# django/contrib/staticfiles/apps.py
class StaticFilesConfig(AppConfig):
name = "django.contrib.staticfiles"
ignore_patterns = ["CVS", ".*", "*~"]
def ready(self):
checks.register(check_finders, checks.Tags.staticfiles)
checks.register(check_storages, checks.Tags.staticfiles)
These checks catch common configuration errors, such as:
staticfiles.E001:STATICFILES_DIRSis not a list or tuple.staticfiles.E002:STATIC_ROOTis accidentally included withinSTATICFILES_DIRS.staticfiles.E003: A prefix inSTATICFILES_DIRSends with a slash.