mirror of
https://github.com/django/django.git
synced 2024-11-24 20:07:01 +01:00
Fixed #34235 -- Added ManifestFilesMixin.manifest_hash attribute.
This adds ManifestFilesMixin.manifest_hash attribute exposing a "hash" of the full manifest. This allows applications to determine when their static files have changed.
This commit is contained in:
parent
75500feecd
commit
afa2e28205
@ -439,7 +439,7 @@ class HashedFilesMixin:
|
||||
|
||||
|
||||
class ManifestFilesMixin(HashedFilesMixin):
|
||||
manifest_version = "1.0" # the manifest format standard
|
||||
manifest_version = "1.1" # the manifest format standard
|
||||
manifest_name = "staticfiles.json"
|
||||
manifest_strict = True
|
||||
keep_intermediate_files = False
|
||||
@ -449,7 +449,7 @@ class ManifestFilesMixin(HashedFilesMixin):
|
||||
if manifest_storage is None:
|
||||
manifest_storage = self
|
||||
self.manifest_storage = manifest_storage
|
||||
self.hashed_files = self.load_manifest()
|
||||
self.hashed_files, self.manifest_hash = self.load_manifest()
|
||||
|
||||
def read_manifest(self):
|
||||
try:
|
||||
@ -461,15 +461,15 @@ class ManifestFilesMixin(HashedFilesMixin):
|
||||
def load_manifest(self):
|
||||
content = self.read_manifest()
|
||||
if content is None:
|
||||
return {}
|
||||
return {}, ""
|
||||
try:
|
||||
stored = json.loads(content)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
else:
|
||||
version = stored.get("version")
|
||||
if version == "1.0":
|
||||
return stored.get("paths", {})
|
||||
if version in ("1.0", "1.1"):
|
||||
return stored.get("paths", {}), stored.get("hash", "")
|
||||
raise ValueError(
|
||||
"Couldn't load manifest '%s' (version %s)"
|
||||
% (self.manifest_name, self.manifest_version)
|
||||
@ -482,7 +482,14 @@ class ManifestFilesMixin(HashedFilesMixin):
|
||||
self.save_manifest()
|
||||
|
||||
def save_manifest(self):
|
||||
payload = {"paths": self.hashed_files, "version": self.manifest_version}
|
||||
self.manifest_hash = self.file_hash(
|
||||
None, ContentFile(json.dumps(sorted(self.hashed_files.items())).encode())
|
||||
)
|
||||
payload = {
|
||||
"paths": self.hashed_files,
|
||||
"version": self.manifest_version,
|
||||
"hash": self.manifest_hash,
|
||||
}
|
||||
if self.manifest_storage.exists(self.manifest_name):
|
||||
self.manifest_storage.delete(self.manifest_name)
|
||||
contents = json.dumps(payload).encode()
|
||||
|
@ -336,6 +336,14 @@ argument. For example::
|
||||
Support for finding paths to JavaScript modules in ``import`` and
|
||||
``export`` statements was added.
|
||||
|
||||
.. attribute:: storage.ManifestStaticFilesStorage.manifest_hash
|
||||
|
||||
.. versionadded:: 4.2
|
||||
|
||||
This attribute provides a single hash that changes whenever a file in the
|
||||
manifest changes. This can be useful to communicate to SPAs that the assets on
|
||||
the server have changed (due to a new deployment).
|
||||
|
||||
.. attribute:: storage.ManifestStaticFilesStorage.max_post_process_passes
|
||||
|
||||
Since static files might reference other static files that need to have their
|
||||
|
@ -201,6 +201,10 @@ Minor features
|
||||
replaces paths to JavaScript modules in ``import`` and ``export`` statements
|
||||
with their hashed counterparts.
|
||||
|
||||
* The new :attr:`.ManifestStaticFilesStorage.manifest_hash` attribute provides
|
||||
a hash over all files in the manifest and changes whenever one of the files
|
||||
changes.
|
||||
|
||||
:mod:`django.contrib.syndication`
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"paths": {
|
||||
"dummy.txt": "dummy.txt"
|
||||
}
|
||||
}
|
@ -436,7 +436,7 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase):
|
||||
# The in-memory version of the manifest matches the one on disk
|
||||
# since a properly created manifest should cover all filenames.
|
||||
if hashed_files:
|
||||
manifest = storage.staticfiles_storage.load_manifest()
|
||||
manifest, _ = storage.staticfiles_storage.load_manifest()
|
||||
self.assertEqual(hashed_files, manifest)
|
||||
|
||||
def test_manifest_exists(self):
|
||||
@ -463,7 +463,7 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase):
|
||||
|
||||
def test_parse_cache(self):
|
||||
hashed_files = storage.staticfiles_storage.hashed_files
|
||||
manifest = storage.staticfiles_storage.load_manifest()
|
||||
manifest, _ = storage.staticfiles_storage.load_manifest()
|
||||
self.assertEqual(hashed_files, manifest)
|
||||
|
||||
def test_clear_empties_manifest(self):
|
||||
@ -476,7 +476,7 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase):
|
||||
hashed_files = storage.staticfiles_storage.hashed_files
|
||||
self.assertIn(cleared_file_name, hashed_files)
|
||||
|
||||
manifest_content = storage.staticfiles_storage.load_manifest()
|
||||
manifest_content, _ = storage.staticfiles_storage.load_manifest()
|
||||
self.assertIn(cleared_file_name, manifest_content)
|
||||
|
||||
original_path = storage.staticfiles_storage.path(cleared_file_name)
|
||||
@ -491,7 +491,7 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase):
|
||||
hashed_files = storage.staticfiles_storage.hashed_files
|
||||
self.assertNotIn(cleared_file_name, hashed_files)
|
||||
|
||||
manifest_content = storage.staticfiles_storage.load_manifest()
|
||||
manifest_content, _ = storage.staticfiles_storage.load_manifest()
|
||||
self.assertNotIn(cleared_file_name, manifest_content)
|
||||
|
||||
def test_missing_entry(self):
|
||||
@ -535,6 +535,29 @@ class TestCollectionManifestStorage(TestHashedFiles, CollectionTestCase):
|
||||
2,
|
||||
)
|
||||
|
||||
def test_manifest_hash(self):
|
||||
# Collect the additional file.
|
||||
self.run_collectstatic()
|
||||
|
||||
_, manifest_hash_orig = storage.staticfiles_storage.load_manifest()
|
||||
self.assertNotEqual(manifest_hash_orig, "")
|
||||
self.assertEqual(storage.staticfiles_storage.manifest_hash, manifest_hash_orig)
|
||||
# Saving doesn't change the hash.
|
||||
storage.staticfiles_storage.save_manifest()
|
||||
self.assertEqual(storage.staticfiles_storage.manifest_hash, manifest_hash_orig)
|
||||
# Delete the original file from the app, collect with clear.
|
||||
os.unlink(self._clear_filename)
|
||||
self.run_collectstatic(clear=True)
|
||||
# Hash is changed.
|
||||
_, manifest_hash = storage.staticfiles_storage.load_manifest()
|
||||
self.assertNotEqual(manifest_hash, manifest_hash_orig)
|
||||
|
||||
def test_manifest_hash_v1(self):
|
||||
storage.staticfiles_storage.manifest_name = "staticfiles_v1.json"
|
||||
manifest_content, manifest_hash = storage.staticfiles_storage.load_manifest()
|
||||
self.assertEqual(manifest_hash, "")
|
||||
self.assertEqual(manifest_content, {"dummy.txt": "dummy.txt"})
|
||||
|
||||
|
||||
@override_settings(STATICFILES_STORAGE="staticfiles_tests.storage.NoneHashStorage")
|
||||
class TestCollectionNoneHashStorage(CollectionTestCase):
|
||||
|
Loading…
Reference in New Issue
Block a user