Djangoチュートリアル⑦（静的ファイル）

# はじめに

[Djangoチュートリアル⑥（自動テスト）](https://qiita.com/honda28/items/f21fe43d29ffe7e15b5d)に続いて、[はじめての Django アプリ作成、その 6](https://docs.djangoproject.com/ja/3.1/intro/tutorial06/)に沿ってチュートリアルを進めていく。チュートリアルだけでは理解できないので、[現場で使える Django の教科書（基礎編）](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Django-%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8%E3%80%8A%E5%9F%BA%E7%A4%8E%E7%B7%A8%E3%80%8B-%E6%A8%AA%E7%80%AC-%E6%98%8E%E4%BB%81/dp/4802094744)も読みながら進める。

# 静的ファイル

Web アプリケーションでは完全な Web ページをレンダリングするために、画像、JavaScript、CSS など必要なファイルをリクエストに応じて中身を変更することなく提供する場合がある。Django では、これらのファイルを "静的 (static) ファイル" と呼ぶ。


# 静的ファイルのプロジェクト構成

チュートリアルでは、各アプリケーション単位で静的ファイルディレクトリ static を作成している。具体的には、polls/static/polls ディレクトリを作成し、style.css などの使用する静的ファイルを置いていく。しかし、[Djangoチュートリアル④（テンプレート）](https://qiita.com/honda28/items/f62dc31e470ef3901706)でテンプレートのプロジェクト構成に関して述べた理由と同じ理由で、この構成はあまり適切でない。そこで、ここではベースディレクトリ直下に static ディレクトリを作成し、そのサブディレクトリとして各アプリケーション単位のテンプレートを扱うようにした。実際の構成を以下に示す。

```config/settings.py
mysite       (<- ベースディレクトリ)
|-- manage.py
|-- config   (<- 設定ディレクトリ)
|   |-- __init__.py
|   |-- asgi.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
|-- polls    (<- アプリケーション)
|   |-- __init__.py
|   |-- admin.py
|   |-- apps.py
|   |-- migrations
|   |   `-- __init__.py
|   |-- models.py
|   |-- tests.py
|   `-- views.py
|-- templates (<- テンプレート)
|   `-- polls
|       `-- index.html
`-- static    (<- 静的ファイル)
    `-- polls
        `--style.css
```

上記構成にすることで、わかりやすく、かつ各アプリケーションの静的ファイルをベースディレクトリから一元管理することができる。この構成で静的ファイルを扱うために、config/settings.py でいくつか設定を行う。まずベースディレクトリやプロジェクト名を設定する。PROJECT_NAME 以外はデフォルトで作成されているので、PROJECT_NAME のみ追記すればよい。

```python:config/settings.py
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
PROJECT_NAME = BASE_DIR.name
### 以下の書き方でもよい
# BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# PROJECT_NAME = os.path.basename(BASE_DIR)
```

BASE_DIR は Path(config/settings.py).resolve() で実行ファイルの絶対パスを取得し、parent でその親ディレクトリを取得している。つまり、プロジェクト名である mysite までの絶対パスを設定している。PROJECT_NAME は BASE_DIR のディレクトリ名 mysite のみを取得している。

続いて、静的ファイルに関する設定を行う。具体的には STATIC_URL はデフォルトのままで、STATICFILES_DIRS と STATIC_ROOT を新たに追加する。

```python:config/settings.py
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = '/var/www/{}/static'.format(PROJECT_NAME)
```

上記コードで設定した項目の意味は以下である。

- STATIC_URL：静的ファイルの配信用のディレクトリで、URL の一部になる。
- STATICFILES_DIRS：アプリケーションに紐付かない静的ファイルを置くディレクトリ。
- STATIC_ROOT：静的ファイルの配信元。静的ファイルの集約用のコマンド collectstatic を実行時に静的ファイルを集約する際のコピー先。詳細は後述。

STATICFILES_DIRS を上記のように指定することで、ベースディレクトリである mysite 以下に作成した static ディレクトリ内の静的ファイルを探索するようになる。
STATIC_ROOT は DEBUG が FALSE 、つまり開発環境ではなく__本番環境で動作させることを想定した設定__である。DEBUG が FALSE のときには、自動配信がされないため静的ファイルを自前で配信するために集約させる必要があり、その配信元が STATIC_ROOT である。 Web アプリケーションでは慣例的に /var/www 以下に配信用のファイルを置くらしく、上記のような設定としている。（Web アプリケーション開発未経験のため知らず。。。）
DEBUG が TRUE のときには、開発用 Web サーバーがベースディレクトリ直下の static ディレクトリなどから静的ファイルを自動配信してくれるためこの設定は必要ない。

また詳細はあまり理解できていないが、config/settings.py の INSTALLED_APPS にデフォルトで設定されている django.contrib.staticfiles が、上記で行った各種設定をもとに静的ファイルの管理をしてくれているらしい。


# アプリのカスタマイズ

上記のプロジェクト構成としたのち、static/polls/style.css を作成して以下コードを書く。

```python:static/polls/style.css
li a {
    color: green;
}
```

さらに templates/polls/index.html の上部に以下コードを追記する。

```templates/polls/index.html
{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">
```

{% static %} テンプレートタグは、静的ファイルの配信 URL を取得するためのタグである。上記コードの {% static 'polls/style.css' %} で先ほど作成した static/polls/style.css を参照している。static タグは Django デフォルトで使えるタグではないため、{% load static %} でロードしている。

上記コードを記入後、`$ python manage.py runserver` で開発用サーバーを起動して、[http://localhost:8000/polls/](http://localhost:8000/polls/) にアクセスすると、スタイルシートが読み込まれていることを確認できる。

# 背景画像の追加

次に、画像のためのサブディレクトリを作る。images サブディレクトリを static/polls ディレクトリの中に作成して、このディレクトリの中に、background.gif を置く。つまり、画像は static/polls/images/background.gif に置かれる。

さらに、static/polls/style.css に次のコードを追加する。

```python:static/polls/style.css
body {
    background: white url("images/background.gif") no-repeat;
}
```

[http://localhost:8000/polls/](http://localhost:8000/polls/) をリロードすると、画像が表示されることを確認できる。

# STATIC_ROOT への集約

本番環境での実行は現状できないが、設定した STATIC_ROOT へ静的ファイルの集約を行う collectstatic コマンドだけでも実行してみる。（`sudo` を付けない場合、`PermissionError: [Errno 13] Permission denied: '/var/www/mysite'` が出る。）

```
$ cd mysite
$ sudo python3 manage.py collectstatic
```

上記実行後、STATIC_ROOT に設定した /var/www/mysite/static/polls に作成した静的ファイルがコピーされていることが確認できる。その他にも admin というディレクトリもあり、いくつか静的ファイルがコピーされていた。

（2021/04/14 追記）
仮想環境では `$ sudo python3 manage.py collectstatic` を実行時にエラーが生じる場合がある。その場合は `$ sudo ~/[virtual_env]/bin/python manage.py collectstatic` のように仮想環境の python を指定してやればよい。詳細は [DjangoでAdminLTE3を扱う](https://qiita.com/honda28/items/729883c0b5048e978edb)を参照。

# おわりに

本番環境などについて[現場で使える Django の教科書（基礎編）](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Django-%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8%E3%80%8A%E5%9F%BA%E7%A4%8E%E7%B7%A8%E3%80%8B-%E6%A8%AA%E7%80%AC-%E6%98%8E%E4%BB%81/dp/4802094744)を参考に書いたが、自身で試せていないので実施して書きたい。続きである[はじめての Django アプリ作成、その 7](https://docs.djangoproject.com/ja/3.1/intro/tutorial07/)ではカスタマイズのチュートリアルがあるが、そのあたりは実際に作りたいアプリを作成しながら理解していこうと考えているので、チュートリアルとしてはここまでとする。

関連記事のリンクは以下。
- [Djangoチュートリアル①（インストールとプロジェクトの作成）](https://qiita.com/honda28/items/34dceaeeb1cdddb4c9dd)
- [Djangoチュートリアル②（ビューの作成）](https://qiita.com/honda28/items/c91f78238ea3d53e776d)
- [Djangoチュートリアル③（モデルの作成、Django Admin）](https://qiita.com/honda28/items/04bc74c28b816aa10e2c)
- [Djangoチュートリアル④（テンプレート）](https://qiita.com/honda28/items/f62dc31e470ef3901706)
- [Djangoチュートリアル⑤（汎用ビュー）](https://qiita.com/honda28/items/7150a5df0213dc6a3927)
- [Djangoチュートリアル⑥（自動テスト）](https://qiita.com/honda28/items/f21fe43d29ffe7e15b5d)