When specifying paths to assets like JavaScript files, but also CSS, the 
Django documentation states: 

> Any links to the file in the codebase should point to the compressed 
> version. 
> <https://docs.djangoproject.com/en/3.0/internals/contributing/writing-code/javascript/#javascript-patches>


This statement however does not properly reflect Django's internal handling 
of such assets, for instance in
https://github.com/django/django/blob/master/django/contrib/admin/helpers.py#L83-L84
https://github.com/django/django/blob/master/django/contrib/admin/options.py#L639-L648
https://github.com/django/django/blob/master/django/contrib/admin/options.py#L2026-L2028
https://github.com/django/django/blob/master/django/contrib/admin/widgets.py#L446-L460
we see, that Django only serves the minimized version of the file, when not 
in DEBUG mode.

This inconsistent handling of assets can cause other problems for third 
party apps, if they follow the documentation as show in the first link.
If in their Media definition they refer to the minimized version of a 
Django asset, say 'admin/js/vendor/jquery/jquery.min.js', then 
the automatic sorting does not work anymore in DEBUG mode. If on the other 
side they refer to 'admin/js/vendor/jquery/jquery.js',
then automatic sorting fails in production. 

There are two solutions to this problem.

*Either*

The documentation explicitly states that when a Media definition refers to 
internal Django assets, one *must* distinguish between the
minimized and unminimized version, just as Django's internal Media 
definitions do (see code examples above).
This however can easily be forgotten and errors therefore become visible 
only after deployment.

*Or*

We rewrite the FileSystemFinder and AppDirectoriesFinder so that in DEBUG 
mode, Django looks if an unminimized version
of the same asset exists, and if so it then serves that. Doing that 
automatically is easy, because the general convention is, that the .min
infix is always placed immediately before the .js- or .css file extension.

In many of my projects I therefore use these two alternative finders, which 
do exactly that:

import os
from django.conf import settings
from django.contrib.staticfiles.finders import (FileSystemFinder as 
FileSystemFinderBase, AppDirectoriesFinder as AppDirectoriesFinderBase)


class FileSystemFinder(FileSystemFinderBase):
    """
    In debug mode, serve /static/any/asset.min.ext as /static/any/asset.ext
    """
    locations = []
    serve_unminimized = getattr(settings, 'DEBUG', False)

    def find_location(self, root, path, prefix=None):
        if self.serve_unminimized:
            # search for the unminimized version, and if it exists, return it
            base, ext = os.path.splitext(path)
            base, minext = os.path.splitext(base)
            if minext == '.min':
                unminimized_path = super().find_location(root, base + ext, 
prefix)
                if unminimized_path:
                    return unminimized_path
        # otherwise proceed with the given one
        path = super().find_location(root, path, prefix)
        return path


class AppDirectoriesFinder(AppDirectoriesFinderBase):
    serve_unminimized = getattr(settings, 'DEBUG', False)

    def find_in_app(self, app, path):
        matched_path = super().find_in_app(app, path)
        if matched_path and self.serve_unminimized:
            base, ext = os.path.splitext(matched_path)
            base, minext = os.path.splitext(base)
            if minext == '.min':
                storage = self.storages.get(app, None)
                path = base + ext
                if storage.exists(path):
                    path = storage.path(path)
                    if path:
                        return path
        return matched_path


In my opinion this approach makes Django's internal and 3rd party package 
code more readable, because it removes the clutter of
case distinction between DEBUG and production mode for each referred 
JavaScript and CSS asset. It also might be less error prone.


-- 
You received this message because you are subscribed to the Google Groups 
"Django developers  (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-developers+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-developers/dd904eeb-a877-48d3-81fa-e72fe71a6735%40googlegroups.com.

Reply via email to