web-dev-qa-db-ja.com

django静的ファイルのバージョン管理

私は静的ファイルとその中の更新に関する問題のいくつかの普遍的な解決策に取り組んでいます。

例:/static/styles.cssファイルのあるサイトがあり、そのサイトが長期間使用されていたため、多くの訪問者がこのファイルをブラウザにキャッシュしたとします。

現在、このcssファイルに変更を加えてサーバーで更新していますが、一部のユーザーはまだ古いバージョンを持っています(サーバーから返された変更日にもかかわらず)

明白な解決策は、ファイル/static/styles.css?v=1.1にバージョンを追加することですが、この場合、開発者はこのファイルの変更を追跡し、手動でバージョンを上げる必要があります

2番目の解決策は、ファイルのmd5ハッシュをカウントし、それを/static/styels.css/?v={mdp5hashvalue}というURLに追加することです。

彼らが私がそれを見る可能性のある方法-このようないくつかのテンプレートタグを作成します

{% static_file  "style.css" %}

レンダリングされます

<link src="/static/style.css?v=md5hash">

しかし、私はこのタグがすべてのページの読み込みでmd5を計算することを望んでおらず、Django-cacheにハッシュを格納したくないので、ファイルを更新した後でクリアする必要があります...

何かご意見は ?

32
Pydev UA

Django 1.4に CachedStaticFilesStorage が含まれるようになりました。これは、必要なことを正確に実行します(まあ...mostly)。

Django 2.2 ManifestStaticFilesStorage なので、CachedStaticFilesStorageの代わりに使用する必要があります。

manage.py collectstaticタスクで使用します。すべての静的ファイルは通常どおりアプリケーションから収集されますが、このストレージマネージャーは、名前にMD5ハッシュが付加された各ファイルのコピーも作成します。たとえば、css/styles.cssファイルがあるとすると、css/styles.55e7cbb9ba48.cssのようなファイルも作成されます。

もちろん、あなたが言及したように、問題は、ビューとテンプレートが常に生成する適切なURLを見つけるためにMD5ハッシュを計算したくないことです。解決策はキャッシュです。さて、あなたはキャッシュなしの解決策を求めました、すみません、それが私がほとんどと言った理由です。しかし、実際にはキャッシングを拒否する理由はありません。 CachedStaticFilesStorageは、staticfilesという名前の特定のキャッシュを使用します。デフォルトでは、既存のキャッシュシステムを使用します。しかし、通常のキャッシュを使用したくない場合は、おそらくそれが分散memcacheであり、静的ファイル名を取得するためだけにネットワーククエリのオーバーヘッドを回避したい場合は、特定のRAM 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'
  }
}
31
MiniQuark

Django-compressor のようなものを使用することをお勧めします。この種のものを自動的に処理するだけでなく、ファイルを自動的に結合して縮小し、ページの読み込みを高速化します。

完全に使用しない場合でも、同様のコードを設定するためのガイダンスとしてコードを調べることができます。これは、単純なStackOverflowの回答から得られるものよりも精査されています。

14
Chris Pratt

車輪を再発明して独自の実装を作成するのは悪いことですか?さらに、私は、pythonアプリケーションの代わりに、バックエンドを使用しても、静的ファイルを本番環境で提供するために、低レベルのコード(たとえばnginx)を使用したいと思っています。再計算するため、ブラウザは新しいファイルのみを取得するので、 ここ 自分の視点:

template.html:

{% load md5url %}
<script src="{% md5url "example.js" %}"/>

out 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アプリケーション(具体的にはプロセス)を再起動する必要があります。

8
deathangel908

私は自分のテンプレートタグを使用して、ファイル変更日をURLに追加します: https://bitbucket.org/ad3w/Django-sstatic

8
ssbb

Django 1.7 追加 ManifestStaticFilesStorageCachedStaticFilesStorage のより良い代替手段であり、キャッシュシステムを使用せず、実行時に計算されるハッシュの問題を解決します。

以下は抜粋です ドキュメントから

CachedStaticFilesStorageはお勧めしません–ほとんどの場合、ManifestStaticFilesStorageの方が適しています。 CachedStaticFilesStorageを使用すると、実行時にキャッシュミスによってファイルをハッシュする必要があるため、パフォーマンスが低下します。ネストされたファイルパスの場合にファイルハッシュが正しいことを確認するためにいくつかのファイルアクセスが必要なため、リモートファイルストレージでは、キャッシュミスでファイルをハッシュするためにいくつかのラウンドトリップが必要です。

これを使用するには、次の行をsettings.pyに追加するだけです。

STATICFILES_STORAGE = 'Django.contrib.staticfiles.storage.ManifestStaticFilesStorage'

次に、python manage.py collectstaticを実行します。 MD5を各静的ファイルの名前に追加します。

6
Benoit Blanchon

このソリューションの主な利点は、テンプレートの内容を変更する必要がないことです。

これにより、ビルドバージョンがSTATIC_URLに追加され、Webサーバーは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]+)/(.*)$ $1/$3;
}
2
yedpodtrzitko

@ deathangel908コードの更新があります。今ではS3ストレージでも(そして私が思う他のどのストレージでも)うまく動作します。違いは、ファイルのコンテンツを取得するために静的ファイルストレージを使用することです。 Originalは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)
1
stasdavydov

私はすべてのビューでグローバルベースコンテキストを使用します。静的バージョンをミリ秒に設定します(このように、アプリケーションを再起動するたびに新しいバージョンになります)。

# global base context
base_context = {
    "title": settings.SITE_TITLE,
    "static_version": int(round(time.time() * 1000)),
}

# function to merge context with base context
def context(items: Dict) -> Dict:
    return {**base_context, **items}

# view
def view(request):
    cxt = context({<...>})
    return render(request, "page.html", cxt)

私のpage.htmlは、私のbase.htmlテンプレートを拡張します。

<link rel="stylesheet" type="text/css" href="{% static 'style.css' %}?v={{ static_version }}">

かなりシンプルで仕事をします

1
rptmat57

バージョンのあるURLに常にURLパラメータがあり、メジャーリリースがある場合は常にURLパラメータのバージョンを変更するのはどうですか。 DNSでも。したがって、www.yourwebsite.comwww.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/にしたりできます。

1
Sid

Djangoの振る舞いを拡張するバージョン管理された静的ファイルのURLを作成する単純なテンプレートタグvstatic

_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コードを調整します)。

_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()を呼び出すので、これは1回だけ計算されます。

_STATIC_VERSION = get_current_commit_hash()
_
1
Caumons