управление версиями статических файлов django
Я работаю над универсальным решением проблемы со статическими файлами и обновлениями в нем
пример: допустим, был сайт с/static / styles.css файл - и сайт использовался долгое время-поэтому многие посетители кэшировали этот файл в браузере
теперь мы делаем изменения в этом файле css и обновление на сервере, но у некоторых пользователей все еще есть старая версия (несмотря на дату модификации, возвращенную сервером)
очевидное решение-добавьте некоторую версию в файл / статические / стили.css?v=1.1
но в этом случае разработчик должен отслеживать изменения в этом файле и вручную увеличивать версию
решение 2-подсчитайте md5 хэш файла и добавьте в url /static/styels.в CSS/?v={mdp5hashvalue}
что выглядит намного лучше, но md5 должен быть рассчитан как-то автоматически..
они возможно, как я это вижу-создать некоторый тег шаблона, как это
{% static_file "style.css" %}
чему
<link src="/static/style.css?v=md5hash">
НО, Я не хочу, чтобы этот тег вычислял md5 при каждой загрузке страницы, и я не хочу хранить хэш в django-кэше, потому что тогда нам придется очистить файл после обновления..
какие мысли ?
9 ответов
Django 1.4 теперь включает в себя CachedStaticFilesStorage
что делает именно то, что вам нужно (ну... почти).
вы используете его с manage.py collectstatic
задач. Все статические файлы собираются из ваших приложений, как обычно, но этот менеджер хранения также создает копию каждого файла с хэшем MD5, добавленным к имени. Например, скажем, у вас есть css/styles.css
файл, он также создаст что-то вроде css/styles.55e7cbb9ba48.css
.
конечно, как вы отметили, проблема это то, что вы не хотите, чтобы ваши представления и шаблоны вычисляли хэш MD5 все время, чтобы узнать соответствующие URL-адреса для создания. Решение заключается в кэшировании. Хорошо, вы попросили решение без кэширования, извините, поэтому я сказал почти. Но на самом деле нет причин отказываться от кэширования. CachedStaticFilesStorage
использует определенный кэш с именем staticfiles
. По умолчанию, он будет использовать существующую систему кэша, и вуаля! Но если вы не хотите использовать ваш обычный кэш, возможно, потому, что это распределенный memcache и вы хотите избежать накладных расходов сетевых запросов только для получения статических имен файлов, то вы можете настроить определенный кэш ОЗУ только для staticfiles
. Это проще, чем кажется: проверьте этот отличный блог. Вот как это будет выглядеть:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
},
'staticfiles': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'staticfiles-filehashes'
}
}
Я бы предложил использовать что-то вроде django-компрессор. В дополнение к автоматической обработке этого типа материалов для вас он также автоматически объединит и уменьшит ваши файлы для быстрой загрузки страницы.
даже если вы не используете его полностью, вы можете проверить их код для руководства в настройке чего-то подобного. Это было лучше проверено, чем все, что вы когда-либо получите от простого ответа StackOverflow.
Я использую свой собственный templatetag, который добавляет дату изменения файла в url:https://bitbucket.org/ad3w/django-sstatic
изобретает колесо и создает собственную реализацию, что плохо? Кроме того, я хотел бы, чтобы код низкого уровня (например, nginx) служил моим статическим файлам в производстве вместо приложения python, даже с бэкэндом. И еще одно: я бы хотел, чтобы ссылки оставались неизменными после пересчета, поэтому браузер извлекает только новые файлы. Так что здесь моя точка зрения:
шаблон.HTML-код:
{% load md5url %}
<script src="{% md5url "example.js" %}"/>
из HTML-код:
static/example.js?v=5e52bfd3
settings.py:
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(PROJECT_DIR, 'static')
appname/templatetags/md5url.py:
import hashlib
import threading
from os import path
from django import template
from django.conf import settings
register = template.Library()
class UrlCache(object):
_md5_sum = {}
_lock = threading.Lock()
@classmethod
def get_md5(cls, file):
try:
return cls._md5_sum[file]
except KeyError:
with cls._lock:
try:
md5 = cls.calc_md5(path.join(settings.STATIC_ROOT, file))[:8]
value = '%s%s?v=%s' % (settings.STATIC_URL, file, md5)
except IsADirectoryError:
value = settings.STATIC_URL + file
cls._md5_sum[file] = value
return value
@classmethod
def calc_md5(cls, file_path):
with open(file_path, 'rb') as fh:
m = hashlib.md5()
while True:
data = fh.read(8192)
if not data:
break
m.update(data)
return m.hexdigest()
@register.simple_tag
def md5url(model_object):
return UrlCache.get_md5(model_object)
Примечание, чтобы применить изменения, приложение uwsgi (чтобы быть конкретным процессом) должно быть перезапущено.
главное преимущество этого решения: вам не нужно ничего изменять в шаблонах.
это добавит версию сборки в STATIC_URL
, а затем веб-сервер удалит его с помощью Rewrite
правило.
settings.py
# build version, it's increased with each build
VERSION_STAMP = __versionstr__.replace(".", "")
# rewrite static url to contain the number
STATIC_URL = '%sversion%s/' % (STATIC_URL, VERSION_STAMP)
таким образом, окончательный url будет, например, следующим:
/static/version010/style.css
и тогда у Nginx есть правило, чтобы переписать его обратно в /static/style.css
location /static {
alias /var/www/website/static/;
rewrite ^(.*)/version([\.0-9]+)/(.*)$ /;
}
как насчет того, что у вас всегда есть параметр URL В вашем URL с версией, и всякий раз, когда у вас есть основной выпуск, вы меняете версию в своем параметре URL. Даже в DNS. Так что если www.yourwebsite.com
нагрузки www.yourwebsite.com/index.html?version=1.0
затем после основного выпуска браузер должен загрузить www.yourwebsite.com/index.html?version=2.0
Я думаю, это похоже на ваше решение 1. Вместо отслеживания файлов вы можете отслеживать целые каталоги? Например ratehr, чем /static/style/css?v=2.0
можно сделать /static-2/style/css
или сделать его даже зернистым /static/style/cssv2/
.
существует обновление для кода @deathangel908. Теперь он хорошо работает и с хранилищем S3 (и с любым другим хранилищем, я думаю). Разница заключается в использовании статического хранилища файлов для получения содержимого файла. Оригинал не работает на S3.
appname/templatetags/md5url.py:
import hashlib
import threading
from django import template
from django.conf import settings
from django.contrib.staticfiles.storage import staticfiles_storage
register = template.Library()
class UrlCache(object):
_md5_sum = {}
_lock = threading.Lock()
@classmethod
def get_md5(cls, file):
try:
return cls._md5_sum[file]
except KeyError:
with cls._lock:
try:
md5 = cls.calc_md5(file)[:8]
value = '%s%s?v=%s' % (settings.STATIC_URL, file, md5)
except OSError:
value = settings.STATIC_URL + file
cls._md5_sum[file] = value
return value
@classmethod
def calc_md5(cls, file_path):
with staticfiles_storage.open(file_path, 'rb') as fh:
m = hashlib.md5()
while True:
data = fh.read(8192)
if not data:
break
m.update(data)
return m.hexdigest()
@register.simple_tag
def md5url(model_object):
return UrlCache.get_md5(model_object)
простой templatetag vstatic
это создает версионные статические файлы urls, которые расширяют поведение Django:
from django.conf import settings
from django.contrib.staticfiles.templatetags.staticfiles import static
@register.simple_tag
def vstatic(path):
url = static(path)
static_version = getattr(settings, 'STATIC_VERSION', '')
if static_version:
url += '?v=' + static_version
return url
если вы хотите автоматически установить STATIC_VERSION в текущий хэш git commit, вы можете использовать следующий фрагмент (Python3 code adjust if necessary):
import subprocess
def get_current_commit_hash():
try:
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip().decode('utf-8')
except:
return ''
At settings.py
вызов get_current_commit_hash()
, поэтому это будет рассчитано только один раз:
STATIC_VERSION = get_current_commit_hash()
Джанго 1.7 добавил ManifestStaticFilesStorage
, лучшая альтернатива CachedStaticFilesStorage
это не использует систему кэша и решает проблему хэша, вычисляемого во время выполнения.
вот выдержка из документации:
CachedStaticFilesStorage не рекомендуется - почти во всех случаях ManifestStaticFilesStorage является лучшим выбором. Существует несколько штрафов за производительность, когда используя CachedStaticFilesStorage с кэша требует хеширование файлов во время выполнения. Удаленное хранилище файлов требует нескольких обходов, чтобы хэшировать файл на пропуске кэша, так как требуется несколько обращений к файлам, чтобы убедиться, что хэш файла верен в случае вложенных путей к файлам.
чтобы использовать это, просто добавьте следующую строку settings.py
:
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
и затем, run python manage.py collectstatic
; Он добавит MD5 к имени каждого статического файла.