【Django】staticファイルの設定と読み込まれない時の対処


投稿日 2023年7月16日 >> 更新日 2024年7月7日

概要

staticファイルが正常に読み込まれるように設定します。

ステータス404で読み込まれていない問題があった場合の対処も解消していきます。

クライアントテスト時のエラーについても対応します。

実行環境&使用ライブラリ

実行環境
Windows Subsystem for Linux 2
Python 3.7.0
使用ライブラリ ライセンス
django==2.2.5 BSD

staticファイルの設定

staticファイルの設定は、開発者によって開発環境(ローカル)と本番環境(リモート)で異なる場合があります。

開発環境ではプロジェクトに配置しているstaticファイルを読み込みますが、本番環境ではミドルウェアのNginxかApacheによって配信することになります。

開発環境の場合

Djangoでstaticファイルを使いたい場合、staticファイルの作成とHTMLファイルにstaticディレクトリを読み込む為の組む込みタグとリンクタグを追記することによって簡単に表示することができます。

デフォルトの「settings.py」は以下のようになっています。

# settings.py

DEBUG = True

ALLOWED_HOSTS = []
...

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    ...
]
...

STATIC_URL = '/static/'

DEBUGが「True」に設定してあれば開発モードということになるので何のエラーも起こらずにプロジェクトを起動できます。

STATIC_URLはSTATIC_ROOTで設定しているstaticファイルを参照するためのURLを定義します。

STATIC_ROOTはDjangoコマンドの「collectstatic」を実行した際に収集されたstaticファイルを配置するディレクトリを定義します。

なのでSTATIC_URLは開発モード問わず特にデフォルトから変更する必要は無いかと思います。

Djangoが開発環境でstaticファイルを読み込んでくれるルートはDjangoアプリの「static」ディレクトリです。

Djangoアプリのディレクトリ内に「static」ディレクトリを作成する必要があります。

そして、「static」ディレクトリ内に必要なstaticファイルを作成します。

app/static
└── app
    └── style.css

HTML側には、Djangoのstaticファイルが参照できるように組み込みタグとリンクタグを追記します。

<!-- app/templates/app/get.html -->

{% load static %}
<html>
  <head>
    <link rel="stylesheet" href="{% static 'app/style.css' %}">
  </head>
  <body>
    <h1>Hello Django Static Files.</h1>
  </body>
</html>

ブラウザからstaticファイルの参照先を見ると以下のように表示されます。

STATIC_URLの設定値を「staticfiles_dir」に設定してみます。

# settings.py

...

# STATIC_URL = '/static/'
STATIC_URL = '/staticfiles_dir/'

するとブラウザから見えるURLが変更しているのが分かります。

ディレクトリ名は変わりましたが、staticファイルは問題無く読み込まれています。

staticファイルの読み込み先であるディレクトリを変更したい場合は、「STATICFILES_DIRS」に絶対パスを指定します。

以下はDjangoプロジェクトの直下からstaticファイルを読み込むような設定です。

# settings.py

...

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

Djangoプロジェクトにstaticディレクトリを配置します。

.
├── app
├── config
├── db.sqlite3
├── manage.py
└── static # appディレクトリから移動
    └── app
        └── style.css

本番環境の場合

開発工程で編集されたDjangoの設定ファイルは以下のようになっているとします。

# settings.py

DEBUG = True

ALLOWED_HOSTS = []
...

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    ...
]
...

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]

Djangoプロジェクトのディレクトリは以下のようになっているとします。

.
├── app
├── config
├── db.sqlite3
├── manage.py
└── static
    └── app
        └── style.css

本番環境下でDjangoプロジェクトを動かす場合は、DEBUGを「False」に設定し、ALLOWED_HOSTSにサーバーのドメイン等を設定します。

# settings.py

DEBUG = False

ALLOWED_HOSTS = ["*"]

DEBUGを「False」に設定してDjangoプロジェクトを起動すると、staticファイルの配信はSTATIC_ROOTの設定値に変更されるので、DjangoアプリやDjangoプロジェクトに配置されているstaticファイルは読み込まれなくなります。

なので本番環境でstaticファイルを設定するには、STATIC_ROOTにDjangoプロジェクト内のstaticファイル群を配置する絶対パスを設定します。

以下は公式のDjangoがSTATIC_ROOTの設定値としている絶対パス。

# settings.py

...
STATIC_ROOT = '/var/www/static/'

Webサイトを動かしているOSは殆どがUnixまたはLinuxディストリビューションだと思いますが、Linuxにおける「/var/」ディレクトリは可変データやログファイルを格納するためのディレクトリとなっていることからstaticファイルのようなデータも「/var/」に格納するのが望ましいのかもしれません。

STATIC_ROOTの「/var/www/static/」にDjangoプロジェクト内のstaticファイルを収集するには、Djangoコマンドの「collectstatic」を実行します。

$ python3 manage.py collectstatic

「PermissionError: [Errno 13] Permission denied: '/var/www/static'」のようなエラーが出た場合は、root権限が必要なので「sudo」を使って実行します。

$ sudo python3 manage.py collectstatic

120 static files copied to '/var/www/static'.

上記を実行すると「120個のstaticファイルが/var/www/staticにコピーされました」となりますが、adminページに使用されているstaticファイル群もまとめてコピーされているので120個という数になります。

これで本番環境のDjangoプロジェクト側の準備は終わりです。

配信方法はNginxやApache等でやりかたは違いますが、Nginxでの配信方法は以下のように設定します。


server {
    ...
    location /static {
        alias /var/www/static;
    }
    ...
}

ステータスコード404で気を付けたい事

staticファイル等がサーバーから読み込めない時はステータスコード404が発生します。

...
[21/Jul/2023 12:08:47] "GET / HTTP/1.1" 200 147
[21/Jul/2023 12:08:47] "GET /static/app/style.css HTTP/1.1" 404 1760

読み込めない際に確認しておきたい事は以下です。

  • DEBUGの設定値:開発環境では「True」、本番環境では「False」

  • 組み込みタグの設定:staticファイルを読み込みたいHTMLファイルの上部に「{% load static %}」の組み込みタグが間違いなく記述されているか

  • リンクタグの設定:staticファイルを読み込みたいHTMLファイルのheadタグにstaticディレクトリから始まる相対パスが記述されているか(「{% static 'app/style.css' %}」static/~のディレクトリを記述)

  • ディレクトリ名や配置の確認:Djangoプロジェクトの直下にstaticディレクトリを置いている場合は、STATICFILES_DIRSに「[os.path.join(BASE_DIR, 'static')]」とリストで設定する必要があります。

  • 本番環境の場合:collectstaticを実行したか

  • 本番環境の場合:STATIC_URLの値と、STATIC_ROOTに設定する最下層の値は当てはまっているか(例:STATIC_URL='static'、STATIC_ROOT='.../.../static/')

  • 本番環境の場合:ミドルウェア(NginxかApache)でコードミスが無いか確認

成果物をデプロイする際に毎回手動で設定ファイルを切り替えるのは手間がかかるので、開発環境と本番環境共に同じ設定ファイル「settings.py」を使えるようにしてみます。

ここでは、PCの環境変数「NAME」を使って開発と本番で「settings.py」の設定値を切り替えたいと思います。

まずホームディレクトリにある隠しファイル「.bash_profile」に以下を追記します。

$ vi ~/.bash_profile
# .bash_profile

export NAME='PC名'

システムを再起動します。

$ source ~/.bash_profile

「env」コマンドで設定されている環境変数を確認できます。

$ env
...
NAME=PC名
...

「settings.py」は以下のような内容になります。

# settings.py

import os

DEBUG = False

ALLOWED_HOSTS = ["*"]
...

INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    ...
]
...

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = '/var/www/static/'

# 開発環境の設定
# 環境変数の「NAME」に設定されている値
ENV_NAME = "PC名"

# 環境変数「NAME」と「ENV_NAME」が同じなら開発モードで実行
if os.getenv("NAME") == ENV_NAME:
    DEBUG = True
    ALLOWED_HOSTS = []
    STATIC_ROOT = None

開発者が使用するPCが一台だけならば「ENV_NAME」は固定値で良いですが、複数人(異なるPC)で開発を行う場合は「ENV_NAME」を変更するだけで済むので簡易的な開発が送れるかと思います。

テスト時のstaticファイルに関するエラーの対応

クライアントテストを実行した際に、「AssertionError: ...:Missing staticfiles manifest entry for 'css/mysite.css'」のようなエラーが発生した場合は、staticファイル(例:css/mysite.css)が読み込まれるように設定する必要があります。

なぜ開発環境では正常に読み込まれていたのにテスト環境では読み込まれなくなったのか。

その原因は、テスト環境では「DEBUG=False」として実行されるからです。

DEBUGが「True」であればDjangoプロジェクト内に配置されたstaticディレクトリを参照しますが、DEBUGが「False」になるとSTATIC_ROOTに定められたディレクトリからの配信になります。

STATIC_ROOTが以下のように設定されているsettings.pyの例。

# mysite/settings.py

import os

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

...
...

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static")

上記のように設定している場合はDjangoプロジェクト直下にあるstaticファイルを参照するようになっているので、「collectstatic」コマンドを実行してプロジェクト直下にstaticファイルを収集します。

$ python manage.py collectstatic

$ python manage.py test

プロジェクト直下ではなく、「/var/www/static」のような設定値にしている場合は、デバッグモードでの「test」コマンドを実行します。

$ python manage.py test --debug-mode

これにより、DEBUGが「True」でテストが実行されるようになります。

それでは以上となります。

最後までご覧いただきありがとうございました。