Commit f62145aa by David Trowbridge

Merge pull request #548 from chipx86/static-view

Add a view for collecting static files before serving them.
parents b072c5a2 00c580ac
......@@ -6,6 +6,7 @@ from collections import OrderedDict
from django.contrib.staticfiles import finders
from django.contrib.staticfiles.storage import staticfiles_storage
from django.utils import six
from pipeline.finders import PipelineFinder
......@@ -26,7 +27,7 @@ class Collector(object):
for d in dirs:
self.clear(os.path.join(path, d))
def collect(self, request=None):
def collect(self, request=None, files=[]):
if self.request and self.request is request:
return
self.request = request
......@@ -41,10 +42,17 @@ class Collector(object):
prefixed_path = os.path.join(storage.prefix, path)
else:
prefixed_path = path
if prefixed_path not in found_files:
if (prefixed_path not in found_files and
(not files or prefixed_path in files)):
found_files[prefixed_path] = (storage, path)
self.copy_file(path, prefixed_path, storage)
if files and len(files) == len(found_files):
break
return six.iterkeys(found_files)
def copy_file(self, path, prefixed_path, source_storage):
# Delete the target file if needed or break
if not self.delete_file(path, prefixed_path, source_storage):
......
from __future__ import unicode_literals
from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured
from django.views.static import serve
from .collector import default_collector
from .conf import settings
def serve_static(request, path, insecure=False, **kwargs):
"""Collect and serve static files.
This view serves up static files, much like Django's
:py:func:`~django.views.static.serve` view, with the addition that it
collects static files first (if enabled). This allows images, fonts, and
other assets to be served up without first loading a page using the
``{% javascript %}`` or ``{% stylesheet %}`` template tags.
You can use this view by adding the following to any :file:`urls.py`::
urlpatterns += static('static/', view='pipeline.views.serve_static')
"""
# Follow the same logic Django uses for determining access to the
# static-serving view.
if not django_settings.DEBUG and not insecure:
raise ImproperlyConfigured("The staticfiles view can only be used in "
"debug mode or if the --insecure "
"option of 'runserver' is used")
if not settings.PIPELINE_ENABLED and settings.PIPELINE_COLLECTOR_ENABLED:
# Collect only the requested file, in order to serve the result as
# fast as possible. This won't interfere with the template tags in any
# way, as those will still cause Django to collect all media.
default_collector.collect(request, files=[path])
return serve(request, path, document_root=django_settings.STATIC_ROOT,
**kwargs)
......@@ -7,6 +7,7 @@ if sys.platform.startswith('win'):
os.environ.setdefault('NUMBER_OF_PROCESSORS', '1')
from .test_collector import *
from .test_compiler import *
from .test_compressor import *
from .test_template import *
......@@ -15,3 +16,4 @@ from .test_middleware import *
from .test_packager import *
from .test_storage import *
from .test_utils import *
from .test_views import *
from __future__ import unicode_literals
import os
from django.contrib.staticfiles import finders
from django.test import TestCase
from pipeline.collector import default_collector
from pipeline.finders import PipelineFinder
class CollectorTest(TestCase):
def tearDown(self):
super(CollectorTest, self).tearDown()
default_collector.clear()
def test_collect(self):
self.assertEqual(
set(default_collector.collect()),
set(self._get_collectable_files()))
def test_collect_with_files(self):
self.assertEqual(
set(default_collector.collect(files=[
'pipeline/js/first.js',
'pipeline/js/second.js',
])),
set([
'pipeline/js/first.js',
'pipeline/js/second.js',
]))
def _get_collectable_files(self):
for finder in finders.get_finders():
if not isinstance(finder, PipelineFinder):
for path, storage in finder.list(['CVS', '.*', '*~']):
if getattr(storage, 'prefix', None):
yield os.path.join(storage.prefix, path)
else:
yield path
from __future__ import unicode_literals
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings
from pipeline.collector import default_collector
from pipeline.views import serve_static
from tests.utils import pipeline_settings
@override_settings(DEBUG=True)
@pipeline_settings(PIPELINE_COLLECTOR_ENABLED=True, PIPELINE_ENABLED=False)
class ServeStaticViewsTest(TestCase):
def setUp(self):
super(ServeStaticViewsTest, self).setUp()
self.filename = 'pipeline/js/first.js'
self.storage = staticfiles_storage
self.request = RequestFactory().get('/static/%s' % self.filename)
default_collector.clear()
def tearDown(self):
super(ServeStaticViewsTest, self).tearDown()
default_collector.clear()
staticfiles_storage._setup()
def test_found(self):
self._test_found()
def test_not_found(self):
self._test_not_found('missing-file')
@override_settings(DEBUG=False)
def test_debug_false(self):
with self.assertRaises(ImproperlyConfigured):
serve_static(self.request, self.filename)
self.assertFalse(self.storage.exists(self.filename))
@override_settings(DEBUG=False)
def test_debug_false_and_insecure(self):
self._test_found(insecure=True)
@pipeline_settings(PIPELINE_ENABLED=True)
def test_pipeline_enabled_and_found(self):
self._write_content()
self._test_found()
@pipeline_settings(PIPELINE_ENABLED=True)
def test_pipeline_enabled_and_not_found(self):
self._test_not_found(self.filename)
@pipeline_settings(PIPELINE_COLLECTOR_ENABLED=False)
def test_collector_disabled_and_found(self):
self._write_content()
self._test_found()
@pipeline_settings(PIPELINE_COLLECTOR_ENABLED=False)
def test_collector_disabled_and_not_found(self):
self._test_not_found(self.filename)
def _write_content(self, content='abc123'):
"""Write sample content to the test static file."""
with self.storage.open(self.filename, 'w') as f:
f.write(content)
def _test_found(self, **kwargs):
"""Test that a file can be found and contains the correct content."""
response = serve_static(self.request, self.filename, **kwargs)
self.assertEqual(response.status_code, 200)
self.assertTrue(self.storage.exists(self.filename))
if hasattr(response, 'streaming_content'):
content = b''.join(response.streaming_content)
else:
content = response.content
with self.storage.open(self.filename) as f:
self.assertEqual(f.read(), content)
def _test_not_found(self, filename):
"""Test that a file could not be found."""
self.assertFalse(self.storage.exists(filename))
with self.assertRaises(Http404):
serve_static(self.request, filename)
self.assertFalse(self.storage.exists(filename))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment