├── LICENSE
├── README.rst
├── dj_static.py
└── setup.py
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013, Kenneth Reitz
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright notice,
10 | this list of conditions and the following disclaimer in the documentation
11 | and/or other materials provided with the distribution.
12 |
13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | DJ-Static
2 | =========
3 |
4 | This is a simple Django middleware utility that allows you to properly
5 | serve static assets from production with a WSGI server like Gunicorn.
6 |
7 | .. note:: You should probably use `WhiteNoise `_ instead. It's better software.
8 |
9 | Django `doesn't recommend `_
10 | the production use of its static file server for a number of reasons.
11 | There exists, however, a lovely WSGI application aptly named `Static `_.
12 |
13 | .. image:: http://farm8.staticflickr.com/7387/8907351990_58677d7c35_z.jpg
14 |
15 | "Finally, a super-simple way of serving assets in Django that’ll actually perform well" — `@jacobian `_
16 |
17 | It is suitable for the production use of static file serving, unlike Django.
18 | Enjoy!
19 |
20 | Shouldn't I use a CDN?
21 | ----------------------
22 |
23 | If you have to ask that question, there's actually quite a good chance you don't.
24 | Static responses aren't very different than dynamic ones.
25 |
26 | If you're running a top-tier application, optimizing for delivery and reducing
27 | frontend load, you will want to explore using a CDN with
28 | `Django-Storages `_.
29 |
30 |
31 | Usage
32 | -----
33 |
34 | ::
35 |
36 | $ pip install dj-static
37 |
38 | Configure your static assets in ``settings.py``::
39 |
40 | STATIC_ROOT = 'staticfiles'
41 | STATIC_URL = '/static/'
42 |
43 | Then, update your ``wsgi.py`` file to use dj-static::
44 |
45 | from django.core.wsgi import get_wsgi_application
46 | from dj_static import Cling
47 |
48 | application = Cling(get_wsgi_application())
49 |
50 | File uploads (optional)
51 | ^^^^^^^^^^^^^^^^^^^^^^^
52 |
53 | In case you also want to serve media files that were uploaded to ``MEDIA_ROOT``::
54 |
55 | MEDIA_ROOT = 'media'
56 | MEDIA_URL = '/media/'
57 |
58 | Then again, update your ``wsgi.py`` file::
59 |
60 | from django.core.wsgi import get_wsgi_application
61 | from dj_static import Cling, MediaCling
62 |
63 | application = Cling(MediaCling(get_wsgi_application()))
64 |
--------------------------------------------------------------------------------
/dj_static.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import static
4 |
5 | from django.conf import settings
6 | from django.core.handlers.wsgi import WSGIHandler
7 | from django.contrib.staticfiles.handlers import StaticFilesHandler as DebugHandler
8 |
9 | try:
10 | from urllib.parse import urlparse
11 | except ImportError: # Python 2
12 | from urlparse import urlparse
13 | from django.contrib.staticfiles import utils
14 |
15 | try:
16 | from django.core.handlers.wsgi import get_path_info
17 | except ImportError: # django < 1.7
18 | try:
19 | from django.core.handlers.base import get_path_info
20 | except ImportError: # django < 1.5
21 | import sys
22 | py3 = sys.version_info[0] == 3
23 |
24 | def get_path_info(environ):
25 | """
26 | Returns the HTTP request's PATH_INFO as a unicode string.
27 | """
28 | path_info = environ.get('PATH_INFO', str('/'))
29 | # Under Python 3, strings in environ are decoded with ISO-8859-1;
30 | # re-encode to recover the original bytestring provided by the web server.
31 | if py3:
32 | path_info = path_info.encode('iso-8859-1')
33 | # It'd be better to implement URI-to-IRI decoding, see #19508.
34 | return path_info.decode('utf-8')
35 |
36 |
37 | class Cling(WSGIHandler):
38 | """WSGI middleware that intercepts calls to the static files
39 | directory, as defined by the STATIC_URL setting, and serves those files.
40 | """
41 | def __init__(self, application, base_dir=None, base_url=None, ignore_debug=False):
42 | self.application = application
43 | self.ignore_debug = ignore_debug
44 | if not base_dir:
45 | base_dir = self.get_base_dir()
46 | self.base_url = urlparse(base_url or self.get_base_url())
47 |
48 | self.cling = static.Cling(base_dir)
49 | try:
50 | self.debug_cling = DebugHandler(application, base_dir=base_dir)
51 | except TypeError:
52 | self.debug_cling = DebugHandler(application)
53 |
54 | super(Cling, self).__init__()
55 |
56 | def get_base_dir(self):
57 | return settings.STATIC_ROOT
58 |
59 | def get_base_url(self):
60 | utils.check_settings()
61 | return settings.STATIC_URL
62 |
63 | @property
64 | def debug(self):
65 | return settings.DEBUG
66 |
67 | def _transpose_environ(self, environ):
68 | """Translates a given environ to static.Cling's expectations."""
69 | environ['PATH_INFO'] = environ['PATH_INFO'][len(self.base_url[2]) - 1:]
70 | return environ
71 |
72 | def _should_handle(self, path):
73 | """Checks if the path should be handled. Ignores the path if:
74 |
75 | * the host is provided as part of the base_url
76 | * the request's path isn't under the media path (or equal)
77 | """
78 | return path.startswith(self.base_url[2]) and not self.base_url[1]
79 |
80 | def __call__(self, environ, start_response):
81 | # Hand non-static requests to Django
82 | try:
83 | if not self._should_handle(get_path_info(environ)):
84 | return self.application(environ, start_response)
85 | except UnicodeDecodeError:
86 | # Apparently a malformed URL. Just hand it to Django
87 | # for it to respond as it sees fit.
88 | return self.application(environ, start_response)
89 |
90 | # Serve static requests from static.Cling
91 | if not self.debug or self.ignore_debug:
92 | environ = self._transpose_environ(environ)
93 | return self.cling(environ, start_response)
94 | # Serve static requests in debug mode from StaticFilesHandler
95 | else:
96 | return self.debug_cling(environ, start_response)
97 |
98 |
99 | class MediaCling(Cling):
100 |
101 | def __init__(self, application, base_dir=None):
102 | super(MediaCling, self).__init__(application, base_dir=base_dir)
103 | # override callable attribute with method
104 | self.debug_cling = self._debug_cling
105 |
106 | def _debug_cling(self, environ, start_response):
107 | environ = self._transpose_environ(environ)
108 | return self.cling(environ, start_response)
109 |
110 | def get_base_dir(self):
111 | return settings.MEDIA_ROOT
112 |
113 | def get_base_url(self):
114 | return settings.MEDIA_URL
115 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | dj-static
4 | ~~~~~~~~~
5 |
6 | This is a simple Django middleware utility that allows you to properly
7 | serve static assets from production with a WSGI server like Gunicorn.
8 |
9 | Django `doesn't recommend `_
10 | the production use of its static file server for a number of reasons.
11 | There exists, however, a lovely WSGI application aptly named `Static `_.
12 |
13 | It is suitable for the production use of static file serving, unlike Django.
14 |
15 | Usage
16 | -----
17 |
18 | Configure your static assets in ``settings.py``::
19 |
20 | STATIC_ROOT = 'staticfiles'
21 | STATIC_URL = '/static/'
22 |
23 | Then, update your ``wsgi.py`` file to use dj-static::
24 |
25 | from django.core.wsgi import get_wsgi_application
26 | from dj_static import Cling
27 |
28 | application = Cling(get_wsgi_application())
29 |
30 | """
31 |
32 | from setuptools import setup
33 |
34 | setup(
35 | name='dj-static',
36 | version='0.0.6',
37 | url='https://github.com/kennethreitz/dj-static',
38 | license='BSD',
39 | author='Kenneth Reitz',
40 | author_email='me@kennethreitz.com',
41 | description='Serve production static files with Django.',
42 | long_description=__doc__,
43 | py_modules=['dj_static'],
44 | zip_safe=False,
45 | install_requires=['static'],
46 | include_package_data=True,
47 | platforms='any',
48 | classifiers=[
49 | 'Environment :: Web Environment',
50 | 'Intended Audience :: Developers',
51 | 'License :: OSI Approved :: BSD License',
52 | 'Operating System :: OS Independent',
53 | 'Programming Language :: Python',
54 | 'Programming Language :: Python :: 3',
55 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
56 | 'Topic :: Software Development :: Libraries :: Python Modules'
57 | ]
58 | )
59 |
--------------------------------------------------------------------------------