summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJarosław Wygoda <jaroslaw@wygoda.me>2021-06-02 21:44:59 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-08-05 12:40:34 +0200
commitd3c4696596822281c0a5d4b9e3ee27732a4ce092 (patch)
treea9cf2c603c8d9938a83132c0dcc1ac46eea125b5
parent4fe3774c729f3fd5105b3001fe69a70bdca95ac3 (diff)
Fixed #27590 -- Allowed customizing a manifest file storage in ManifestFilesMixin.
-rw-r--r--AUTHORS1
-rw-r--r--django/contrib/staticfiles/storage.py13
-rw-r--r--docs/ref/contrib/staticfiles.txt20
-rw-r--r--docs/releases/4.0.txt5
-rw-r--r--tests/staticfiles_tests/test_storage.py52
5 files changed, 85 insertions, 6 deletions
diff --git a/AUTHORS b/AUTHORS
index 3a8df03ca3..2d9d23f73e 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -423,6 +423,7 @@ answer newbie questions, and generally made Django that much better:
Jan Rademaker
Jarek Głowacki <jarekwg@gmail.com>
Jarek Zgoda <jarek.zgoda@gmail.com>
+ Jarosław Wygoda <jaroslaw@wygoda.me>
Jason Davies (Esaj) <https://www.jasondavies.com/>
Jason Huggins <http://www.jrandolph.com/blog/>
Jason McBrayer <http://www.carcosa.net/jason/>
diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py
index fe13423749..7cf43ed6ab 100644
--- a/django/contrib/staticfiles/storage.py
+++ b/django/contrib/staticfiles/storage.py
@@ -401,13 +401,16 @@ class ManifestFilesMixin(HashedFilesMixin):
manifest_strict = True
keep_intermediate_files = False
- def __init__(self, *args, **kwargs):
+ def __init__(self, *args, manifest_storage=None, **kwargs):
super().__init__(*args, **kwargs)
+ if manifest_storage is None:
+ manifest_storage = self
+ self.manifest_storage = manifest_storage
self.hashed_files = self.load_manifest()
def read_manifest(self):
try:
- with self.open(self.manifest_name) as manifest:
+ with self.manifest_storage.open(self.manifest_name) as manifest:
return manifest.read().decode()
except FileNotFoundError:
return None
@@ -435,10 +438,10 @@ class ManifestFilesMixin(HashedFilesMixin):
def save_manifest(self):
payload = {'paths': self.hashed_files, 'version': self.manifest_version}
- if self.exists(self.manifest_name):
- self.delete(self.manifest_name)
+ if self.manifest_storage.exists(self.manifest_name):
+ self.manifest_storage.delete(self.manifest_name)
contents = json.dumps(payload).encode()
- self._save(self.manifest_name, ContentFile(contents))
+ self.manifest_storage._save(self.manifest_name, ContentFile(contents))
def stored_name(self, name):
parsed_name = urlsplit(unquote(name))
diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt
index 14c739b3ea..46a73e7c54 100644
--- a/docs/ref/contrib/staticfiles.txt
+++ b/docs/ref/contrib/staticfiles.txt
@@ -313,6 +313,20 @@ For example, the ``'css/styles.css'`` file with this content:
@import url("../admin/css/base.27e20196a850.css");
+You can change the location of the manifest file by using a custom
+``ManifestStaticFilesStorage`` subclass that sets the ``manifest_storage``
+argument. For example::
+
+ from django.conf import settings
+ from django.contrib.staticfiles.storage import (
+ ManifestStaticFilesStorage, StaticFilesStorage,
+ )
+
+ class MyManifestStaticFilesStorage(ManifestStaticFilesStorage):
+ def __init__(self, *args, **kwargs):
+ manifest_storage = StaticFilesStorage(location=settings.BASE_DIR)
+ super().__init__(*args, manifest_storage=manifest_storage, **kwargs)
+
.. versionchanged:: 4.0
Support for finding paths in the source map comments was added.
@@ -320,6 +334,8 @@ For example, the ``'css/styles.css'`` file with this content:
Support for finding paths to JavaScript modules in ``import`` and
``export`` statements was added.
+ The ``manifest_storage`` argument was added.
+
.. attribute:: storage.ManifestStaticFilesStorage.max_post_process_passes
Since static files might reference other static files that need to have their
@@ -384,6 +400,10 @@ hashing algorithm.
Use this mixin with a custom storage to append the MD5 hash of the file's
content to the filename as :class:`~storage.ManifestStaticFilesStorage` does.
+.. versionchanged:: 4.0
+
+ The ``manifest_storage`` argument was added.
+
Finders Module
==============
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index 390c561638..0fa87b2810 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -174,6 +174,11 @@ Minor features
replaces paths to JavaScript modules in ``import`` and ``export`` statements
with their hashed counterparts.
+* The new ``manifest_storage`` argument of
+ :class:`~django.contrib.staticfiles.storage.ManifestFilesMixin` and
+ :class:`~django.contrib.staticfiles.storage.ManifestStaticFilesStorage`
+ allows customizing the manifest file storage.
+
:mod:`django.contrib.syndication`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/staticfiles_tests/test_storage.py b/tests/staticfiles_tests/test_storage.py
index d5aa1c1c9d..0a34f64394 100644
--- a/tests/staticfiles_tests/test_storage.py
+++ b/tests/staticfiles_tests/test_storage.py
@@ -1,3 +1,4 @@
+import json
import os
import shutil
import sys
@@ -13,7 +14,7 @@ from django.contrib.staticfiles.management.commands.collectstatic import (
Command as CollectstaticCommand,
)
from django.core.management import call_command
-from django.test import override_settings
+from django.test import SimpleTestCase, override_settings
from .cases import CollectionTestCase
from .settings import TEST_ROOT
@@ -499,6 +500,55 @@ class TestCollectionSimpleStorage(CollectionTestCase):
self.assertIn(b"other.deploy12345.css", content)
+class CustomManifestStorage(storage.ManifestStaticFilesStorage):
+ def __init__(self, *args, manifest_storage=None, **kwargs):
+ manifest_storage = storage.StaticFilesStorage(
+ location=kwargs.pop('manifest_location'),
+ )
+ super().__init__(*args, manifest_storage=manifest_storage, **kwargs)
+
+
+class TestCustomManifestStorage(SimpleTestCase):
+ def setUp(self):
+ self.manifest_path = Path(tempfile.mkdtemp())
+ self.addCleanup(shutil.rmtree, self.manifest_path)
+
+ self.staticfiles_storage = CustomManifestStorage(
+ manifest_location=self.manifest_path,
+ )
+ self.manifest_file = self.manifest_path / self.staticfiles_storage.manifest_name
+ # Manifest without paths.
+ self.manifest = {'version': self.staticfiles_storage.manifest_version}
+ with self.manifest_file.open('w') as manifest_file:
+ json.dump(self.manifest, manifest_file)
+
+ def test_read_manifest(self):
+ self.assertEqual(
+ self.staticfiles_storage.read_manifest(),
+ json.dumps(self.manifest),
+ )
+
+ def test_read_manifest_nonexistent(self):
+ os.remove(self.manifest_file)
+ self.assertIsNone(self.staticfiles_storage.read_manifest())
+
+ def test_save_manifest_override(self):
+ self.assertIs(self.manifest_file.exists(), True)
+ self.staticfiles_storage.save_manifest()
+ self.assertIs(self.manifest_file.exists(), True)
+ new_manifest = json.loads(self.staticfiles_storage.read_manifest())
+ self.assertIn('paths', new_manifest)
+ self.assertNotEqual(new_manifest, self.manifest)
+
+ def test_save_manifest_create(self):
+ os.remove(self.manifest_file)
+ self.staticfiles_storage.save_manifest()
+ self.assertIs(self.manifest_file.exists(), True)
+ new_manifest = json.loads(self.staticfiles_storage.read_manifest())
+ self.assertIn('paths', new_manifest)
+ self.assertNotEqual(new_manifest, self.manifest)
+
+
class CustomStaticFilesStorage(storage.StaticFilesStorage):
"""
Used in TestStaticFilePermissions