Django、AWS S3、WhiteNoiseを使ったPythonでの静的ファイルの配信

Webサイトでは一般に、画像、CSS、JavaScriptなど、ブラウザで完全なWebページを表示するために必要なファイルが追加で必要になります。

小さなプロジェクトでは、リソースを絶対パスで指定したり、HTMLファイル内にCSSやJavaScriptの関数をインラインで記述することで、回避することができます。

しかし、これはコーディングのベストプラクティスに反するだけでなく、大規模なプロジェクト、特に複数のアプリケーションを扱う場合には、厄介なことになります。

Django では、インタラクティブなユーザ体験、ドキュメントのプレゼンテーション、機能的な Web ページに必要なファイルを静的ファイルと呼びます。

この記事では、各アプリケーションが提供する複数の静的ファイルのセットをどのように扱えば、Webサイトのルック&フィールをカスタマイズできるかを見ていきます。

静的ファイルの設定

Django は静的ファイルをどのように提供するかについて、非常に大きな柔軟性を提供し ています。

ここでは、ローカル開発での静的ファイルの使用と、少し複雑な実運用での静的ファイルの使用を取り上げます。

まず最初に、基本的な設定を行いましょう。

Django は django.contrib.staticfiles を提供しており、各アプリケーションから (そしてあなたが指定した他の場所からも) 静的ファイルを集め、実運用で簡単に提供できるようにします。

settings.pyファイルで、INSTALLED_APPS` は以下のようになっているはずです。

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.admin',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles', # To serve static files
]


STATIC_ROOTは、静的ファイルを収集する場所を定義するためのパスです。

settings.py で、 STATIC_ROOT への絶対パスを指定します。

これを行うには、 os モジュールの dirname() 関数を使用して、これらのファイルを格納するディレクトリの名前を取得し、パスを定義します。

import os


PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'staticfiles')


次に、静的ファイルを参照するときに使用する URL である STATIC_URL を指定する必要があります。

None以外の値を指定する場合は、末尾に/をつけなければなりません。

以下のパスは、静的ファイルをhttp://localhost:8000/static/またはhttp://127.0.0.1:8000/static/` という場所に格納することを意味する。

STATIC_URL = '/static/'


Django には STATICFILES_FINDERS として、静的ファイルを探すのに使うファインダーのリストがあります。

デフォルトのファインダは AppDirectoriesFinder で、 INSTALLED_APPS の中にある static という名前のフォルダを探します。

例えば、プロジェクトに users というアプリケーションが含まれている場合、 project_name/users/static/index.css というディレクトリを作って、そのアプリケーションに関連する CSS ファイルを追加することができます。

この場合でも、 project_name/users/static/users/index.css のように、アプリケーション名で別のサブディレクトリを作成する方がよいでしょう。

これは、似たような名前の静的ファイルが2つ以上ある場合に重要です。

各アプリに index.css があり、それぞれ異なる CSS スタイルを含んでいると考えましょう。

Django は app/static/ ディレクトリの中から、最初に見つかった index.css を探します。

これでは、各アプリケーションの static ディレクトリにある様々な index.css を区別することができません。

そのため、アプリケーション名 app/static/app/ を持つサブディレクトリを作成しました。

なお、多くのプロジェクトでは、複数のアプリケーションで共通の静的ファイルを持つことができるので、通常は、アプリケーションごとに static フォルダを作成するのではなく、プロジェクトのルートディレクトリに static フォルダを作成した方がよいでしょう。

プロジェクトのディレクトリにある全ての静的ファイルを共通の場所に置くには、 AppDirectoriesFinderapp ディレクトリにある static しか探さないので、 STATICFILES_DIRS を設定して、新しいディレクトリを Django に通知する必要があります。

静的ファイルを置く場所を複数定義することもできます。

複数のプロジェクトがある場合、個々のプロジェクトの静的フォルダーを定義するのはここです。

STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
    # Extra lookup directories for collectstatic to find static files
)


STATICFILES_DIRSは、STATICFILES_FINDERSからFileSystemFinder` を削除しない場合のみ動作することに注意してください。

簡単にまとめると、私たちの settings.py は以下を含みます。

import os


PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
STATIC_ROOT  = os.path.join(PROJECT_ROOT, 'staticfiles')
STATIC_URL = '/static/'


# Extra lookup directories for collectstatic to find static files
STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
)


これで、静的ファイルをプロジェクトで使用する準備が整いました。

あとは {% load static %}static テンプレートタグをロードして、与えられた相対パスに対して URL をビルドするだけです。

それでは、テンプレートファイル base.html でどのように静的ファイルを利用するか見てみましょう。

<!DOCTYPE html

{% load static %}
<html lang="en"
    {% include 'head.html' %}
 <style
    body{
      background: url('{% static "bg.png" %}') no-repeat center center fixed; 
        -webkit-background-size: cover;
        -moz-background-size: cover;
        -o-background-size: cover;
        background-size: cover;
    }
 </style
<body
<div class="row justify-content-center"
<div class="col-8"
<h1 class="mainbtn"MY CUSTOM CSS CLASS</h1
          {% block content %}
          <hr class="mt-0 mb-4"/
          {% endblock %}
        </div
</div

</body
</html


大きなプロジェクトでは、通常 head タグに長いコードを記述するため、 base.html には head.html テンプレートが含まれ、適切に分離されます。

h1mainbtnクラスはstatic/index.cssファイルで定義されています。

背景画像bg.pngstatic` ディレクトリにあります。

head.html は以下のような構成になっている。

<head
    {% block css_block %}{% endblock %}
    {% load static %}
    <meta charset="utf-8"/
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport"/
<link href="{% static 'css/index.css' %}" rel="stylesheet"/
<script src="{% static 'js/functions.js' %}"</script
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"</script
<title{% block title %} Title to be changed in included files {% endblock %}</title
</head


静的ファイルの提供

上記の設定に加え、実際に静的ファイルを提供する必要があります。

これは、 Debug = True の場合、Django の runserver コマンドで自動的に行われます。

この方法は簡単なので開発段階では使うべきですが、非効率で安全でないため、実運用では推奨されません。

Django には組み込みのコマンド collecstatic があります。

これは全ての静的ファイルを、既に設定した一つのディレクトリ STATIC_ROOT にコンパイルします。

最後に、 collectstatic コマンドで静的ファイルを収集する際に使用するストレージエンジンです。

ストレージエンジンは STATICFILES_STORAGE で設定することができます。

Django は独自のストレージエンジンを持っているので、 STATICFILES_STORAGE のデフォルト値は django.contrib.staticfiles.storage.StaticFilesStorage に設定されています。

本番環境での静的ファイル

静的ファイルを本番環境に置くには、主に2つの手順があります。

  • 静的ファイルが変更されるたびに collectstatic コマンドを実行する。
  • 静的ファイルサーバに STATIC_ROOT を移動して提供するように手配する。

Storageクラスのpost_process()メソッドが 2 番目のステップを担当しますが、実際にはストレージエンジン (例:STATICFILES_STORAGE`) に依存します。

注意: 環境の違いにより、すべてのプロダクションで静的ファイルを提供することは異なりますが、基本的な考え方と手順は同じです。

運用環境で静的ファイルを扱うには、3つの主要な方法があります。

  • 静的ファイルとサイトを同じサーバーから配信する。静的ファイルとサイトを同じサーバーから配信する: 静的ファイルを、すでにウェブアプリケーションを実行しているサーバーから配信したい場合は、この方法を使用します。パフォーマンス上の問題はありますが、ホスティングサーバーが一台で済むため、コスト面では有利です。これを行うには、コードをデプロイサーバーにプッシュしてから collectstatic を実行し、すべてのファイルを STATIC_ROOT にコピーしてください。最後に、静的ファイルを STATIC_URL 配下で提供するようにウェブサーバを設定します。
  • 専用サーバから静的ファイルを提供する。静的ファイル専用サーバーの最も一般的な選択肢は、nginxとApacheのストリップダウン版です。静的ファイルの専用サーバとして最も一般的なのは、nginxとApacheのストリップダウン版です。静的ファイルが変更されるたびにローカルで collectstatic を実行し、 STATIC_ROOT を専用サーバーのディレクトリにプッシュして、サービスを提供します。詳細な手順については、各サーバーのドキュメントを参照してください。
  • クラウドサービスから静的ファイルを提供する。クラウドサービスからの静的ファイルの提供:もう一つの一般的な戦術は、Amazon、Microsoft Azure、Alibaba Cloudなどのクラウドストレージプロバイダーから静的ファイルを提供することです。

ここでは、この目的のためにAmazon S3をどのように使用するかを見てみましょう。

まず、以下のコマンドを使用して、2つのPythonライブラリをインストールします。

$ python -m pip install boto3
$ pip install django-storages


boto3ライブラリは、Amazon S3やその他のAmazon Web Services (AWS)にアクセスするためのパブリックAPIクライアントである。

django-storages は Amazon S3、OneDrive などのストレージバックエンドを管理する。

これは組み込みの Django ストレージバックエンド API をプラグインしています。

また、 INSTALLED_APPSstorages を追加する必要があります。

今、私たちの INSTALLED_APPS はこのようになっています。

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.sites',
    'django.contrib.contenttypes',
    'django.contrib.admin',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'storages', # New
]


その後、settings.py に以下の設定を追加します。

AWS_ACCESS_KEY_ID = your_access_key_id
AWS_SECRET_ACCESS_KEY = your_secret_access_key
AWS_STORAGE_BUCKET_NAME = 'sibtc-static'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'

STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'


最後に、python manage.py collectstatic を実行すれば、静的ファイル用の Amazon S3 の設定は完了です。

WhiteNoiseを使った静的ファイルの配信

Amazon S3 のようなサードパーティのクラウドサービスは、有料のサブスクリプションを含むいくつかの理由で使わない人がよくいます。

WhiteNoise を使うと、Django プロジェクトが自分自身の静的ファイルを提供できるようになり、サービスプロバイダに依存せずにどこにでもデプロイできる自己完結したユニットになるのです。

WSGI 互換の任意の Web アプリケーションで動作しますが、Django プロジェクトで最も簡単に設定できます。

WhiteNoiseの設定

でWhiteNoiseをインストールしましょう。

$ pip install whitenoise


settings.pyで、WhiteNoise をMIDDLEWARE` のリストに以下の順序で追加してください。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # WhiteNoise Middleware above all but below Security
    'whitenoise.middleware.WhiteNoiseMiddleware', 
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
  ]


圧縮サポートと永久にキャッシュ可能なファイルを使用するには、settings.pyに以下を追加してください。

STATICFILES_STORAGE = ‘whitenoise.storage.CompressedManifestStaticFilesStorage’` を追加します。

python manage.py collectstatic`を実行します。

これで完了です。

これで、Heroku などのホスティングプラットフォームに Web アプリケーションをデプロイできるようになりました。

結論

すべての Web サイト開発者は、美しく機能的な Web サイトを作るために静的ファイルを必要とします。

Django は静的ファイルの簡単な設定を提供するだけでなく、そのデプロイメントで遊べるとてつもない柔軟性も備えています。

この記事では、ローカル開発および実運用において、Django の Web アプリケーションに静的ファイルを統合するいくつかの方法について取り上げました。

タイトルとURLをコピーしました