How Django works (3): Testing Server and Static Files Serving

How Django Handles Static Files

By default, Django installs an Application django.contrib.staticfiles, which is responsible for handling requests to files under STATIC_URL.
 
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls',
)

STATIC_URL = '/static/'


This method is grossly inefficient and probably insecure, so it is unsuitable for production. https://docs.djangoproject.com/en/1.8/howto/static-files/deployment/

How staticfiles App Works

Based on the first blog of Django series, Django interacts with web servers by providing a callable application that conforms to WSGI specification.

1
2
3
4
5
class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):


What staticfiles app does is that it wraps the original callable application.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class StaticFilesHandler(WSGIHandler):
    """
    WSGI middleware that intercepts calls to the static files directory, as
    defined by the STATIC_URL setting, and serves those files.
    """
    # May be used to differentiate between handler types (e.g. in a
    # request_finished signal)
    handles_files = True

    def __init__(self, application):
        self.application = application
        self.base_url = urlparse(self.get_base_url())
        super(StaticFilesHandler, self).__init__()

     ...
    def __call__(self, environ, start_response):
        if not self._should_handle(get_path_info(environ)):
            return self.application(environ, start_response)
        return super(StaticFilesHandler, self).__call__(environ, start_response)     

application is the callable application provided in wsgi.py. Whenever a request is from a webserver, it invokes StaticFilesHandler.__call__ instead of WSGIHandler.__call__.

Line 17 checks to see if the requested path is /static/:
if not true, self.application is called, otherwise super(StaticFilesHandler, self) is called.

Both invocations actually executes the same code:
  • self.application is an object of WSGIHandler, so WSGIHandler.__call__ is executed;
  • StaticFilesHandler inherits WSGIHandler, so super().__call__ refers to WSGIHandler.__call__.

Same Code for Static Files and non Static Files?

How are two invocations different when they are running the same code?
The answer is that they manipulate different data -- one is self.application, and the other self. self.application is an object of WSGIHandler whereas self is an object of StaticFilesHandler.

WSGI Middleware or Convenient Workaround

Calling StaticFilesHandler a WSGI middleware is a misnomer to me, the reason is that in our static "middleware", nothing special happens. The added behavior is provided by WSGIHandler.__call__ processing different data. In other words, StaticFilesHandler must have knowledge of internal workings of WSGIHandler to work, especially how WSGIHandler utilize data in the instance __dict__.

A true middleware should be transparent and only do preprocessing and postprocessing,included but not limited to (see https://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides):
  • Routing a request to different application objects based on the target URL, after rewriting the environ accordingly;
  • Allowing multiple applications or frameworks to run side-by-side in the same process;
  • Load balancing and remote processing, by forwarding requests and responses over a network;
  • Perform content postprocessing, such as applying XSL stylesheets.
In a nutshell, a middleware should treat WSGI callable application as a blackbox. In the case of StaticFilesHandler, it directly inherits from WSGIHandler, the callable application class, it's not a blackbox approach right from the start.

Popular posts from this blog

LeetCode 68 Text Justification (C++, Python)

How Django Works (4) URL Resolution

Python Class Method and Class Only Method