我在Heroku上运行了一个Django应用程序。要存储和提供我的静态文件,我将django-storages与我的S3存储桶以及标准Django ManifestFilesMixin
一起使用。我也使用django-pipeline。
在代码中:
from django.contrib.staticfiles.storage import ManifestFilesMixin
from storages.backends.s3boto import S3BotoStorage
from pipeline.storage import PipelineMixin
class S3PipelineManifestStorage(PipelineMixin, ManifestFilesMixin, S3BotoStorage):
pass
设置有效,但staticfiles.json
清单也存储在S3上。我可以看到两个问题:
我的应用程序的存储实例必须从S3获取staticfiles.json
,而不是仅从本地文件系统获取它。这在性能方面毫无意义。清单文件的唯一消费者是服务器应用程序本身,因此它也可以存储在本地文件系统而不是远程。
我不确定这个问题有多重要,因为我想(或希望)服务器应用程序在阅读一次后缓存该文件。
清单文件在collectstatic
部署期间写入,因此,如果任何已运行的服务器应用程序版本的实例在之前从S3读取清单文件部署结束,新的slug接管,他们可以获取错误的静态文件 - 只应该为新slug的实例提供。
请注意,特别是在Heroku上,新的应用程序实例可以动态弹出,因此即使应用程序确实缓存了清单文件,它的首次提取也可能是部署新的slu ..
所描述的场景特定于Heroku,但我想其他环境会出现类似的问题。
显而易见的解决方案是将清单文件存储在本地文件系统中。每个slug都有自己的清单文件,性能最佳,并且不会有如上所述的任何部署比赛。
有可能吗?
答案 0 :(得分:5)
前段时间我读了this article,我认为这很适合你的情况 在最后一段中存在以下内容:
staticfiles.json位于哪里?
默认情况下,
staticfiles.json
将驻留在STATIC_ROOT
中 收集所有静态文件的目录。
我们在S3存储桶上托管所有静态资产,这意味着默认情况下staticfiles.json
最终会同步到S3。但是,我们希望它存在于代码目录中,以便我们将其打包并将其发送到每个应用服务器。因此,
子类化ManifestStaticFilesStorage
将寻找staticfiles.json
中的STATIC_ROOT
以便阅读映射。我们有 要覆盖此行为,我们将ManifestStaticFilesStorage
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage from django.conf import settings class KoganManifestStaticFilesStorage(ManifestStaticFilesStorage): def read_manifest(self): """ Looks up staticfiles.json in Project directory """ manifest_location = os.path.abspath( os.path.join(settings.PROJECT_ROOT, self.manifest_name) ) try: with open(manifest_location) as manifest: return manifest.read().decode('utf-8') except IOError: return None
通过上述更改,Django静态模板标签现在将读取 来自
staticfiles.json
的映射,它位于项目根目录中。
我自己没有使用它,所以请告诉我它是否有帮助!
答案 1 :(得分:3)
有Django票证#27590解决了这个问题。该票证具有实现解决方案的拉取请求,但尚未进行审核。
答案 2 :(得分:1)
@John和Kogan的答案很好,但没有提供完成此工作所需的全部代码:正如@Danra提到的那样,您还需要将staticfiles.json
保存在源文件夹中,以使这项工作。这是我根据上述答案创建的代码:
import json
import os
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
from whitenoise.storage import CompressedManifestStaticFilesStorage
# or if you don't use WhiteNoiseMiddlware:
# from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
class LocalManifestStaticFilesStorage(CompressedManifestStaticFilesStorage):
"""
Saves and looks up staticfiles.json in Project directory
"""
manifest_location = os.path.abspath(settings.BASE_DIR) # or settings.PROJECT_ROOT depending on how you've set it up in your settings file.
manifest_storage = FileSystemStorage(location=manifest_location)
def read_manifest(self):
try:
with self.manifest_storage.open(self.manifest_name) as manifest:
return manifest.read().decode('utf-8')
except IOError:
return None
def save_manifest(self):
payload = {'paths': self.hashed_files, 'version': self.manifest_version}
if self.manifest_storage.exists(self.manifest_name):
self.manifest_storage.delete(self.manifest_name)
contents = json.dumps(payload).encode('utf-8')
self.manifest_storage._save(self.manifest_name, ContentFile(contents))
现在,您可以将LocalManifestStaticFilesStorage
用于STATICFILES_STORAGE
。运行manage.py collectstatic
时,清单将保存到您的根项目文件夹,而Django在提供内容时会在该文件夹中寻找它。
如果您的部署中包含多个虚拟机,请确保仅运行collectstatic
一次,并将staticfiles.json
文件复制到部署中的所有计算机中,作为代码部署的一部分。这样做的好处是,即使某些计算机尚没有最新的更新,它们仍将提供正确的内容(与代码的当前版本相对应),因此您可以在存在以下情况的地方进行逐步部署:混合状态。