2012-02-03 4 views
24

Я работаю на некоторое универсальное решение для проблемы со статическими файлами и обновлениями в немДжанго статические файлы управления версиями

Пример: позволяет сказать, что там был сайт с /static/styles.css файла - и сайт был использован для долгое время - так много посетителей кэшировать этот файл в браузере

Теперь мы делаем изменения в этом CSS файл, и обновления на сервере, но некоторые пользователи по-прежнему имеют старую версию (несмотря на дату изменения, возвращаемый сервер)

Очевидное решение - добавьте некоторую версию в файл /static/styles.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 на каждой странице, и я не хочу, чтобы хранить хэш в Джанго-кэша, потому что тогда мы должны очистить после обновления файла ..

любые мысли?

+1

Есть уже много решений для этой проблемы, начиная оттуда, например. http://djangopackages.com/grids/g/static-builders/ как @ChrisPratt предлагает! – Stefano

ответ

24

Django 1.4 теперь включает в себя CachedStaticFilesStorage, который делает именно то, что вам нужно (ну ... почти).

Вы используете его с заданием manage.py collectstatic. Все статические файлы собираются из ваших приложений, как обычно, но этот менеджер хранилищ также создает копию каждого файла с хешем MD5, добавленным к имени. Например, скажем, у вас есть файл css/styles.css, он также создаст что-то вроде css/styles.55e7cbb9ba48.css.

Конечно, как вы уже упоминали, проблема в том, что вы не хотите, чтобы ваши взгляды и шаблоны, вычисляющие хеш-память MD5, все время находили подходящие URL-адреса для генерации. Решение - кэширование. Хорошо, вы попросили решение без кэширования, извините, поэтому я сказал почти. Но на самом деле нет причин отказываться от кэширования. CachedStaticFilesStorage использует определенный кэш с именем staticfiles. По умолчанию он будет использовать вашу существующую систему кеширования и voilà! Но если вы не хотите, чтобы он использовал ваш обычный кеш, возможно, потому, что это распределенная memcache, и вы хотите избежать накладных расходов сетевых запросов, чтобы получить статические имена файлов, тогда вы можете настроить конкретный кеш оперативной памяти только для staticfiles. Это проще, чем звучит: посмотрите this excellent blog post. Вот как это будет выглядеть:

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' 
    } 
} 
+0

Для будущих читателей [этот пост] (https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#cachedstaticfilesstorage) помогли мне реализовать это быстро и просто – Anupam

1

Как насчет того, что у вас всегда есть URL-адрес в URL-адресе с версией и всякий раз, когда у вас есть основной выпуск, вы изменяете версию в своем URL-параметре. Даже в DNS. Так что если www.yourwebsite.com загружается www.yourwebsite.com/index.html?version=1.0, то после крупной версии браузер должен загрузить www.yourwebsite.com/index.html?version=2.0

Я думаю, это похоже на ваше решение 1. Вместо отслеживания файлов вы можете отслеживать целые каталоги? Например, скорость выше /static/style/css?v=2.0 вы можете сделать /static-2/style/css или сделать это даже зернистым /static/style/cssv2/.

13

Я бы предложил использовать что-то вроде django-compressor. В дополнение к автоматической обработке этого типа вещей для вас он также автоматически объединяет и уменьшает ваши файлы для быстрой загрузки страницы.

Даже если вы не используете его полностью, вы можете проверить их код для руководства по настройке чего-то подобного. Это было лучше проверено, чем все, что вы когда-либо получали из простого ответа StackOverflow.

+0

Для записи django-компрессор использует для этой цели время модификации файла, аналогично рекомендации в ответе @ssbb - https://github.com/django-compressor/django-compressor/blob/develop/compressor/ storage.py – Wtower

+0

Вы можете использовать это решение, даже если вы выполняете сжатие js вручную в другом месте, используя легкий компрессор с django, например Closure Compiler, с настройкой WHITESPACE_ONLY, чтобы вы могли использовать именование имен файлов static/cache. –

8

Я использую свой собственный templatetag который добавить файл дату изменения в URL: https://bitbucket.org/ad3w/django-sstatic

+1

Лучший ответ когда-либо, вы только что спасли мой день (и часть моей жизни <3). Простой, легкий и простой. –

+0

Вместо того, чтобы строить путь самостоятельно, лучше позволить django искать его: от django.contrib.staticfiles.finders import find full_path = find (path) –

5

Заново изобретает колесо и создает собственную реализацию, что плохо? Кроме того, я бы хотел, чтобы код низкого уровня (например, nginx) служил моим статическим файлам в производстве вместо приложения python, даже с бэкэнд. И еще одно: я хочу, чтобы ссылки оставались неизменными после пересчета, поэтому браузер извлекает только новые файлы. Так here's шахта точка зрения:

template.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') 

имя_приложения/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 (чтобы быть конкретным процессом) должен быть перезапущен.

+0

Это хорошо, но не делает ли этот подход вычисление md5 по каждому запросу? – Wtower

+0

Нет, результат ставится в поля статического класса. Затем мы пытаемся получить от него: cls._md5_sum [файл] – deathangel908

+0

Да, заметил после моего комментария. Благодарю. – Wtower

1

Обновлено для кода @ deathangel908. Теперь он хорошо работает и с хранилищем S3 (и с любым другим хранилищем, которое я думаю). Разница заключается в использовании статического хранилища файлов для получения содержимого файла. Оригинал не работает на S3.

имя_приложения/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) 
2

Основное преимущество этого решения: вы не должны изменять что-либо в шаблонах.

Это добавит версию сборки в STATIC_URL, а затем веб-сервер удалит ее с помощью правила Rewrite.

Настройки.ру

# 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]+)/(.*)$ $1/$3; 
} 
1

Простой templatetag vstatic, который создает информацию о версиях static file 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 к текущему мерзавцу фиксации хэша, вы можете использовать следующий фрагмент код (Python3 коду при необходимости отрегулировать):

import subprocess 


def get_current_commit_hash(): 
    try: 
     return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).strip().decode('utf-8') 
    except: 
     return '' 

В settings.py вызове get_current_commit_hash(), так что это будет рассчитываться только один раз:

STATIC_VERSION = get_current_commit_hash() 
Смежные вопросы