Docker Compose でWebアプリケーションを開発する環境（NGINX + uWSGI + PostgreSQL）を構築する。

# はじめに
　Docker Composeを使って、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを構築し、
　Django（Python）で開発したWebアプリケーションをブラウザで動作確認するまでの手順を記載します。
　※ 本投稿は、Mac OSを前提としています。
　　macOS Catalina 10.15.3で作業しています。

|No.|項目|説明|
|:---|:---|:---|
|1|Docker Compose|複数のDocker コンテナを一元管理するツールです。<br/>本投稿では、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを<br/>構築します。|
|2|NGINX|Webサーバです。<br/>本投稿では、ここに静的ファイルを配置します。|
|3|uWSGI|アプリケーションサーバです。<br/>Pythonで作成したWebアプリケーションを配置します。|
|4|PostgreSQL|DBサーバです。<br/>Webアプリケーションで利用するデータを保持します。|
|5|Django|PythonのWebフレームワークです。|

# 前提条件
　Docker for Macをダウンロードする際、Dockerの公式サイトにログインする必要があります。
　Dockerの公式サイトにログインするためのユーザを作成して下さい。
# 1. Docker for Mac
　最初にDocker for Macのインストールを行います。
　Docker Composeは、Docker for Macに含まれています。
## 1-1. ダウンロード
　以下のサイトからダウンロードします。
　https://docs.docker.com/docker-for-mac/install/

　(1) 「Download from Docker Hub」ボタンを押下します。
　　![FireShot Capture 192 - Install Docker Desktop on _ - https___docs.docker.com_docker-for-mac_install_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/328494b2-745b-6bd6-dfb2-3b059d766499.png)

　(2) 「Download Docker Desktop for Mac」ボタンを押下します。
　　Docker.dmg がダウンロードされます。
　　![FireShot Capture 193 - Docker Hub - https___hub.docker.com__overlay=onboarding.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/68a0922f-5436-0bdf-9d9d-01d65087a93f.png)

## 1-2. インストール
　(1) Docker.dmgの実行
　　ダウンロードした Docker.dmg を実行して下さい。

　(2) Applicationsに移動
　　DockerをApplicationsに移動させます。
　　<img width="714" alt="スクリーンショット 2020-02-09 18.57.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/b9ce98df-7d30-f48a-3323-9fb0f7584f5d.png">

　(3) Dockerの実行
　　アプリケーションからDockerを実行して下さい。

## 1-3. 確認
　(1) ターミナルを開き、"docker --version"を実行します。
　　インストールしたDockerのバージョンを確認します。

```php
$ docker --version
Docker version 19.03.5, build 633a0ea
```

　(2) 次に、"docker-compose --version"を実行します。
　　Docker Composeのバージョンを確認します。

```php
$ docker-compose --version
docker-compose version 1.25.4, build 8d51620a
```

# 2. Docker コンテナの構築
　NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを構築します。
## 2-1. 作業ディレクトリの作成
　任意の作業ディレクトリを作成して下さい。
　本投稿では、"django"を作成します。

```php
$ mkdir django
```

## 2-2. ファイル/ディレクトリの準備
　Docker Composeの実行に必要なファイル、Webアプリケーションのソースコードを
　配置するディレクトリなどを準備します。

　(1) 作業ディレクトリへの移動

```php
$ cd django
```

　(2) requirements.txt の作成

```php
$ touch requirements.txt
```

　(3) Dockerfile の作成

```php
$ touch Dockerfile
```

　(4) docker-compose.yml の作成

```php
$ touch docker-compose.yml
```

　(5) nginx ディレクトリの作成

```php
$ mkdir nginx
```

　(6) nginx/conf ディレクトリの作成

```php
$ mkdir nginx/conf
```

　(7) nginx/conf/mainproject_nginx.conf の作成

```php
$ touch nginx/conf/mainproject_nginx.conf
```

　(8) nginx/uwsgi_params の作成

```php
$ touch nginx/uwsgi_params
```

　(9) src ディレクトリの作成

```php
$ mkdir src
```

　(10) src/static ディレクトリの作成

```php
$ mkdir src/static
```

　(11) dbdata ディレクトリの作成

```php
$ mkdir dbdata
```

　[ファイル/ディレクトリの説明]

|No.|名称|説明|
|:---|:---|:---|
|1|requirements.txt|利用するPythonのパッケージ（ライブラリ）を指定するファイル|
|2|Dockerfile|Docker コンテナの構成情報を記述するためのファイル|
|3|docker-compose.yml|アプリケーションを動かすための処理を記述するファイル|
|4|nginx|NGINXのルートディレクトリ|
|5|nginx/conf|NGINXの設定ファイルを配置するディレクトリ|
|6|nginx/conf/mainproject_nginx.conf|NGINXの設定を記述するためのファイル<br/>※ ファイル名は任意です、拡張子は".conf"にして下さい。|
|7|nginx/uwsgi_params|NGINXとuWSGIを連携させるためのファイル|
|8|src|Webアプリケーションを配置するディレクトリ|
|9|src/static|css、JavaScript、画像などの静的ファイルを配置するディレクトリ|
|10|dbdata|PostgreSQLのデータファイルを保持するディレクトリ|

　[ここまでのファイル構成]

```php
$ tree .
.
├── Dockerfile
├── dbdata
├── docker-compose.yml
├── nginx
│   ├── conf
│   │   └── mainproject_nginx.conf
│   └── uwsgi_params
├── requirements.txt
└── src
    └── static

5 directories, 5 files
```


## 2-3. requirements.txtの設定
　requirements.txtを開き、以下の内容を記述して保存します。
　requirements.txtは、次に説明するDockerfileの中で利用しています。
　（"RUN pip install -r requirements.txt"の箇所）

```php
Django
django-bootstrap4
uwsgi
psycopg2
```

　[説明]
　　① Django
　　　PythonのWebフレームワークのDjangoをインストールします。
　　② django-bootstrap4
　　　DjangoでBootstrap4を利用するためのパッケージをインストールします。
　　　※ Bootstrap4を利用しない場合、インストールする必要はありません。
　　　　最後にDjangoで作成したページにBootstrap4を適用する方法を参考に紹介します。
　　③ uwsgi
　　　アプリケーションサーバをインストールします。
　　④ psycopg2
　　　PythonからPostgreSQLに接続するためのドライバをインストールします。

## 2-4. Dockerfileの設定
　Dockerfileを開き、以下の内容を記述して保存します。
　Dockerfileは、次に説明するdocker-compose.ymlの中で利用しています。
　（"build: ."の箇所）

```php
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
COPY . /code/
```

　[説明]
　　① FROM python:3.7
　　　Docker のベースイメージに"python3.7"を指定
　　　"Docker Hub"のイメージから作成します。
　　② ENV PYTHONUNBUFFERED 1
　　　Python の標準出力、標準エラー出力をバッファに溜め込まない設定となります。
　　　バッファが有効になっていると、標準出力の途中経過が表示されず、
　　　全てのタスクが終わった後に纒めて表示されます。
　　③ RUN mkdir /code
　　　コンテナ上にcodeディレクトリを作成します。
　　④ WORKDIR /code
　　　コンテナの作業ディレクトリにcodeディレクトリを指定します。
　　⑤ COPY requirements.txt /code/
　　　ローカルマシンのrequirements.txtをコンテナのcodeディレクトリに追加します。
　　⑥ RUN pip install --upgrade pip
　　　コンテナ上のpipを最新のバージョンにします。
　　⑦ RUN pip install -r requirements.txt
　　　requirements.txtに記載しているPythonのパッケージ（ライブラリ）を
　　　コンテナ上インストールします。
　　⑧ ローカルマシンのカレントディレクトリの内容をコンテナのcodeディレクトリに追加します。
　
## 2-5. docker-compose.ymlの設定
　docker-compose.ymlを開き、以下の内容を記述して保存します。

```php
version: '3.7'

volumes:
  pgdata:
    driver_opts:
      type: none
      device: $PWD/dbdata
      o: bind
services:
  nginx:
    image: nginx
    container_name: container.nginx
    ports:
      - "8000:8000"
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
      - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
      - ./src/static:/static
      - ./nginx/log:/var/log/nginx
    depends_on:
      - web
  web:
    build: .
    container_name: container.uwsgi
    command: uwsgi --ini /code/mainproject/django.ini
    volumes:
      - ./src:/code
    expose:
      - "8001"
    depends_on:
      - db
  db:
    image: postgres
    restart: always
    container_name: container.postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: "postgresdb"
      POSTGRES_USER: "admin"
      POSTGRES_PASSWORD: "test"
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C"
    volumes:
      - pgdata:/var/lib/postgresql/data
    hostname: postgres
```

　[説明]
　　① version: '3.7'
　　　docker-composeで使用するバージョンとなります。
　　　バージョンによって、docker-compose.ymlの書き方が異なります。
　　　※ Docker 本体のバージョンとの関連は「[Compose file version 3 reference](https://docs.docker.com/compose/compose-file/)」を参照して下さい。
　　② volumes:
　　　トップレベルのvolumesは、名前付きvolumeとなります。
　　　ここでは、"pgdata"という名前付きvolumeを指定しています。
　　　"device: $PWD/dbdata"は、ローカルマシンの作業ディレクトリ配下のdbdataディレクトリを表します。
　　③ services:
　　　構築するコンテナを記述します。
　　　ここでは、"nginx"、"web"、"db"を記述しています。
　　　この名前は任意で指定が可能です。
　　　例えば、"db"は、"database"などでも問題ありません。
　　③-1 nginx:
　　③-1-1 image: nginx
　　　NGINXが動作するコンテナは、"Docker Hub"のイメージから作成します。
　　③-1-2 container_name: container.nginx
　　　"container.nginx"という任意のコンテナ名を付けます。
　　③-1-3 ports:
　　　NGINXのポート番号に8000ポートを指定します。
　　③-1-4 volumes:
　　　ローカルマシンの作業ディレクトリ配下の各ファイル/ディレクトリと
　　　NGINXのコンテナのファイル/ディレクトリのマウントを設定します。
　　　a) ./nginx/conf:/etc/nginx/conf.d
　　　　これにより、"nginx/conf/mainproject_nginx.conf"が"/etc/nginx/conf.d"にマウントされます。
　　　　"/etc/nginx/conf.d"に配置した拡張子".conf"のファイルは、
　　　　親の設定ファイルにあたる"/etc/nginx/nginx.conf"によって読み込まれます。
　　　　* "/etc/nginx/nginx.conf"は、nginxのコンテナにデフォルトで存在します。
　　　b) ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
　　　　"/etc/nginx/uwsgi_params"は、次に説明する"nginx/conf/mainproject_nginx.conf"の中で利用しています。
　　　　（"include     /etc/nginx/uwsgi_params;"の箇所）
　　　c) ./src/static:/static
　　　　静的ファイルを配置するディレクトリをマウントします。
　　　　cssなどをNGINXから配信するための設定となります。
　　　d) ./nginx/log:/var/log/nginx
　　　　ログファイルを配置するディレクトリをマウントします。
　　　　これにより、ローカル上でログを確認できるようになります。
　　③-1-5 depends_on:
　　　"web"、つまりuWSGIのコンテナが起動された後に、NGINXのコンテナを起動するという指定です。
　　③-2 web:
　　③-2-1 build: .
　　　uWSGIが動作するコンテナは、Dockerfileに記述したコンテナの構成情報で作成します。
　　　"build: ."は、ビルドに使用するDockerfileのパスを表します。
　　　つまり、このコンテナは、Dockerfileに記載したコンテナの構成情報で作成します。
　　③-2-2 container_name: container.uwsgi
　　　"container.uwsgi"という任意のコンテナ名を付けます。
　　③-2-3 command: uwsgi --ini /code/mainproject/django.ini
　　　コンテナが起動した後に、uWSGIを設定ファイル"/code/mainproject/django.ini"の
　　　内容に従って、起動させるということを表します。
　　③-2-4 volumes:
　　　ローカルマシンの作業ディレクトリ配下の各ディレクトリと
　　　uWSGIのコンテナのディレクトリのマウントを設定します。
　　③-2-5 expose:
　　　uWSGIのポート番号に8001ポートを指定します。
　　③-2-6 depends_on:
　　　"db"、つまりPostgreSQLのコンテナが起動された後に、uWSGIのコンテナを起動するという指定です。
　　③-3 db:
　　③-3-1 image: postgres
　　　PostgreSQLが動作するコンテナは、"Docker Hub"のイメージから作成します。
　　③-3-2 restart: always
　　　ホストOSを起動したタイミングでコンテナを自動起動するという指定です。
　　③-3-3 container_name: container.postgres
　　　"container.postgres"という任意のコンテナ名を付けます。
　　③-3-4 ports:
　　　PostgreSQLのポート番号に5432ポートを指定します。
　　③-3-5 environment:
　　　a) POSTGRES_DB: "postgresdb"
　　　　コンテナ起動時に作成するデフォルトのDBの名称を指定します。
　　　　指定しない場合、POSTGRES_USERの値が使われます。
　　　b) POSTGRES_USER: "admin"
　　　　スーパユーザの名称を指定します。
　　　　指定しない場合、"postgres"が使われます。
　　　c) POSTGRES_PASSWORD: "test"
　　　　スーパユーザのパスワードを指定します。
　　　　指定しない場合、POSTGRES_USERの値が使われます。
　　　d) POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C"
　　　　DBを作成するコマンド（postgres initdb）を実行する際に渡す引数を指定します。
　　　　エンコーディングに"UTF-8"、ロケールに"C"を指定しています。
　　　※ environmentの詳細は、「[Supported tags and respective Dockerfile links](https://hub.docker.com/_/postgres/) 」を参照して下さい。
　　③-3-6 volumes:
　　　名前付きvolumeの"pgdata"と
　　　PostgreSQLコンテナの"/var/lib/postgresql/data"のマウントを設定します。
　　　この設定により、DBのデータが永続化されます。
　　　（コンテナを削除してもデータがローカルマシン上に残ります。）
　　③-3-7 hostname:
　　　"postgres"という任意のホスト名を付けます。

## 2-6. nginx/conf/mainproject_nginx.confの設定
　nginx/conf/mainproject_nginx.confを開き、以下の内容を記述して保存します。

```php
# the upstream component nginx needs to connect to
upstream django {
    ip_hash;
    server web:8001;
}

# configuration of the server
server {
    # the port your site will be served on
    listen      8000;
    # the domain name it will serve for
    server_name 127.0.0.1; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    location /static {    
        alias /static; 
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
    }
}
```

　[説明]
　　① upstream django
　　　細かい設定方法は割愛しますが、"server web:8001;"については、
　　　docker-compose.ymlに記載したweb（ポート番号は、8001）を指しています。
　　　つまり、uWSGIを指しています。
　　② server
　　　こちらも細かい設定方法は割愛しますが、"location / {"については、
　　　NGINXのルートにアクセスがあった場合、①で説明したuWSGIに処理を渡すという設定になります。
　　　また、③-1-4で説明した/etc/nginx/uwsgi_paramsの設定ファイルに基づいて連携しています。
　　　"location /static {"については、
　　　静的ファイルについては、NGINXのstaticディレクトリに配置したものを使用するという設定です。

## 2-7. nginx/uwsgi_paramsの設定
　nginx/uwsgi_paramsを開き、以下の内容を記述して保存します。

```php
uwsgi_param  QUERY_STRING       $query_string;
uwsgi_param  REQUEST_METHOD     $request_method;
uwsgi_param  CONTENT_TYPE       $content_type;
uwsgi_param  CONTENT_LENGTH     $content_length;

uwsgi_param  REQUEST_URI        $request_uri;
uwsgi_param  PATH_INFO          $document_uri;
uwsgi_param  DOCUMENT_ROOT      $document_root;
uwsgi_param  SERVER_PROTOCOL    $server_protocol;
uwsgi_param  REQUEST_SCHEME     $scheme;
uwsgi_param  HTTPS              $https if_not_empty;

uwsgi_param  REMOTE_ADDR        $remote_addr;
uwsgi_param  REMOTE_PORT        $remote_port;
uwsgi_param  SERVER_PORT        $server_port;
uwsgi_param  SERVER_NAME        $server_name;
```

## 2-9. Djangoプロジェクトの作成
　以下のコマンドを実行し、Djangoプロジェクトを作成します。
　プロジェクト名は任意です。（ここでは、"mainproject"という名前にしました。）

```php
$ docker-compose run --rm web django-admin startproject mainproject .
```

　下記のようなWARNINGが出力されますが、「イメージが存在していなかったので、ビルドした」という
　警告なので、問題ありません。

```php
WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
```

　[実行結果の確認①（ファイル構成）]
　　ファイル構成は以下の構成となります。
　　※ dbdata配下には、マウントしたPostgreSQLのデータ（/var/lib/postgresql/data）が
　　　大量に作成されるので、省略しています。

```php
$ tree .
.
├── Dockerfile
├── dbdata
│   ├── PG_VERSION
│   ├── base
│   │   └── ・・・ 省略
│   │   └── ・・・ 省略
│   └── ・・・ 省略
├── docker-compose.yml
├── nginx
│   ├── conf
│   │   └── mainproject_nginx.conf
│   └── uwsgi_params
├── requirements.txt
└── src
    ├── mainproject
    │   ├── __init__.py
    │   ├── asgi.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    ├── manage.py
    └── static
```

　[実行結果の確認②（Docker コンテナの状態）]
　　"docker ps -a"コマンドを実行し、Docker コンテナの状態を確認して下さい。
　　以下のようにPostgreSQLのコンテナが起動しています。
　　※ uWSGIのDocker コンテナについては、次に説明するuWSGIのiniファイルを作成していない為、
　　　起動していません。（docker-compose.ymlの"command: uwsgi --ini /code/mainproject/django.ini"の箇所）
　　※ NGINXのDocker コンテナについては、uWSGIのDocker コンテナの起動が
　　　前提条件となっていますので、こちらもまだ起動していません。

```php
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
9d68d40adee0        postgres            "docker-entrypoint.s…"   25 hours ago        Up 25 hours         0.0.0.0:5432->5432/tcp   container.postgres
```

## 2-10. uWSGIのiniファイルを作成します。
　src/mainproject/django.iniを新規に作成し、以下の内容を記述します。

```php
[uwsgi]
socket = :8001
module = mainproject.wsgi
wsgi-file = /code/mainproject/wsgi.py
logto = /code/mainproject/uwsgi.log
py-autoreload = 1
```

　[説明]
　　① socket = :8001
　　　uWSGIを起動するポート番号を指定します。
　　② module = mainproject.wsgi
　　　loadするwsgiモジュールを指定します。
　　③ wsgi-file = /code/mainproject/wsgi.py
　　　loadするwsgiファイルを指定します。
　　④ logto = /code/mainproject/uwsgi.log
　　　ログを出力する場所を指定します。
　　　アプリケーションのエラーが発生した時などに確認するログとなります。
　　⑤ py-autoreload = 1
　　　オートリロード機能の間隔を指定します。
　　　この設定では、1秒ごとにリクエストがあればリロードするという設定になります。
　　※ 詳細は、「[uWSGI Options](https://uwsgi-docs.readthedocs.io/en/latest/Options.html) 」を参照して下さい。

## 2-11. ALLOWED_HOST、DB及び静的ファイルの設定を行う。
　src/mainproject/settings.pyを開き、以下の修正を行い保存します。

　(1) import osの追加
　　下記の修正を行います。
　　先頭の概要コメントの後に追加します。
　　※ 2022/09/追加
　　　これを追加しないとwsgi.py実行時にosライブラリが見つからないというエラーが発生する。

　　（修正前）

```php
from pathlib import Path
```

　　（修正後）

```php
import os
from pathlib import Path
```

　(2) ALLOWED_HOSTの設定
　　下記の修正を行います。
　　公開するドメイン名を設定します。
　　※ Django 1.5以降のsettings.pyに、ALLOWED_HOSTSという項目が追加されました。
　　　これを設定しない場合、デバックモードがtrueの時にエラーとなります。

　　（修正前）

```php
ALLOWED_HOSTS = []
```

　　（修正後）

```php
ALLOWED_HOSTS = ["*"]
```

　(3) DBへの接続設定
　　下記の修正を行います。
　　docker-compose.ymlのdbのenvironmentに合わせます。
　　（修正前）

```php
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```

　　（修正後）

```php
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgresdb',
        'USER': 'admin',
        'PASSWORD': 'test',
        'HOST': 'db',
        'PORT': 5432,
    }
}
```

　(4) 静的ファイルの設定
　　下記の修正を行います。

　　（修正前）

```php
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

STATIC_URL = '/static/'
```

　　（修正後）

```php
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/

# All applications can use Static files of this directory
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "mainproject/static/"),
)

# Collectstatic command put STATICFILES_DIRS and each application's Static files together into this directory
STATIC_ROOT = os.path.join(BASE_DIR, 'static/')

# Django template files read this directory to use Static files(example {% static 'style.css' %})
STATIC_URL = '/static/'
```

　[説明]
　　① STATICFILES_DIRS
　　　パスの指定先は任意です。
　　　ここでは、「2-9. Djangoプロジェクトの作成」で説明した
　　　Djangoプロジェクト（mainproject）配下のstaticディレクトリを指定しています。
　　　このディレクトリには、各アプリケーションで共通の静的ファイルを配置します。
　　　後術しますが、本投稿では、faviconを配置します。
　　② STATIC_ROOT
　　　後術するCollectstaticコマンドを実行した際に、
　　　STATICFILES_DIRSで指定した共通の静的ファイルと
　　　各アプリケーション配下のstaticディレクトリの静的ファイルを
　　　収集し、配置するディレクトリを指定します。
　　　BASE_DIRは、作業ディレクトリ（src）のことなので、STATIC_ROOTは、src/staticとなります。
　　　「2-5. docker-compose.ymlの設定」で説明したNGINXの静的ファイルのマウント元と一致します。
　　　（"- ./src/static:/static"の./src/staticの箇所）
　　③ STATIC_URL
　　　後術するDjangoのテンプレートファイルが静的ファイルを読み込むディレクトリ先となります。
　　　「2-5. docker-compose.ymlの設定」で説明したNGINXの静的ファイルのマウント先と一致します。
　　　（"- ./src/static:/static"の/staticの箇所）

　　※ 「[Django Static Files](https://rahmonov.me/posts/django-static-files/) 」を合わせて見ると理解しやすいと思います。

## 2-12. Docker コンテナを起動します。
 以下のコマンドを実行します。

```php
$ docker-compose up -d
```

　[実行結果の確認①（Docker コンテナの状態）]
　　"docker ps -a"コマンドを実行し、Docker コンテナの状態を確認して下さい。
　　以下のようにNGINX、uWSGI、PostgreSQLのコンテナが起動しています。

```php
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                            NAMES
f41f7a5b634d        nginx               "nginx -g 'daemon of…"   10 minutes ago      Up 10 minutes       80/tcp, 0.0.0.0:8000->8000/tcp   container.nginx
c12eee7ac189        django_web          "uwsgi --ini /code/m…"   10 minutes ago      Up 10 minutes       8001/tcp                         container.uwsgi
9d68d40adee0        postgres            "docker-entrypoint.s…"   27 hours ago        Up 27 hours         0.0.0.0:5432->5432/tcp           container.postgres
```

## 2-13. 動作確認
　ブラウザを開き、"[http://0.0.0.0:8000](http://0.0.0.0:8000)"にアクセスし、
　Djangoのデフォルトのページが表示されることを確認します。
　　![FireShot Capture 001 - Django_ the Web framework for perfectionists with deadlines. - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/c093ce63-6272-acce-ff88-6c601ef55e55.png)

　ここまでの手順で、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナの構築が完了しました。

# 3. Django Webアプリケーションの作成
　Djangoで"Hello world."を出力するWebアプリケーションを作成します。

## 3-1. Djangoアプリケーションの作成
　以下のコマンドを実行し、Djangoアプリケーションを作成します。
　アプリケーション名は任意です。（ここでは、"app"という名前にしました。）

```php
$ docker-compose run --rm web python manage.py startapp app
```

　[実行結果の確認①（ファイル構成）]
　　srcディレクトリ配下にappディレクトリが作成されます。

```php
$ tree src
src
├── app
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── mainproject
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-37.pyc
│   │   ├── settings.cpython-37.pyc
│   │   ├── urls.cpython-37.pyc
│   │   └── wsgi.cpython-37.pyc
│   ├── asgi.py
│   ├── django.ini
│   ├── settings.py
│   ├── urls.py
│   ├── uwsgi.log
│   └── wsgi.py
├── manage.py
└── static

5 directories, 19 files
```

## 3-2. ビューの作成
　"Hello world."を出力するファイルです。
　src/app/views.pyを開き、以下の修正を行い保存します。

　　（修正前）

```php
from django.shortcuts import render

# Create your views here.
```

　　（修正後）

```php
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello world.")
```

## 3-3. URLの対応付け
　作成したビューをブラウザからアクセスするために、 URLの対応付けを行う必要があります。

　(1) appディレクトリの配下にurls.pyを新規作成します。
　　urls.pyを作成することで、URLの対応付けに必要なURLconfが作成されます。
　　以下の内容を記述します。

```php
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

　(2) mainprojectディレクトリの配下のurls.pyを更新します。
　　ルート（プロジェクト）のURLconfにapp.urlsモジュールの記述を反映させます。
　　具体的には、
　　　① django.urls.includeのimportを追加
　　　② urlpatternsのリストにinclude('app.urls')を挿入
　　を行います。
　　※ 以下の修正前後のファイルは、文頭のコメント部分は省略しています。
　　※ include()関数は他のURLconfへの参照が可能になります。
　　　修正後の"path('app/', include('app.urls'))"の箇所は、
　　　app/にアクセスがあった場合、app配下のURLconfを参照するということを表します。

　　（修正前）

```php
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]
```

　　（修正後）

```php
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('app/', include('app.urls')),
    path('admin/', admin.site.urls),
]
```

## 3-4. 動作確認
　ブラウザを開き、"[http://0.0.0.0:8000/app/](http://0.0.0.0:8000/app/)"にアクセスし、
　画面に"Hello world."が表示されることを確認します。

# 4. Databaseの設定
　DjangoでDatabase（PostgreSQL）を利用するための設定を行います。
　※ 私がCafe好きなので、Cafeの情報を例に説明します。

## 4-1. モデルの作成
　Webアプリケーションで利用するモデルを最初に定義します。
　ここでは、Area、Cafe、Utilityの3つのモデルを作成します。
　src/app/models.pyを開き、以下の内容を記述して保存します。

　　（修正前）

```php
from django.db import models

# Create your models here.
```

　　（修正後）

```php
from django.db import models

class Area(models.Model):
    name = models.CharField(max_length=100)
    create_date = models.DateTimeField('date published')

    def __str__(self):
        return self.name;

class Cafe(models.Model):
    area = models.ForeignKey(Area, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    memo = models.CharField(max_length=400)
    website = models.URLField()
    image_path = models.CharField(max_length=400)
    create_date = models.DateTimeField('date published')

    def __str__(self):
        return self.name;

class Utility(models.Model):
    key = models.CharField(max_length=100)
    value = models.CharField(max_length=100)
    create_date = models.DateTimeField('date published')

    def __str__(self):
        return self.key;
```

　[説明]
　　① Area
　　　Cafeのエリア情報（場所）を管理するマスタです。
　　② Cafe
　　　Cafeの情報を保持するテーブルです。
　　　※ ForeignKeyを使用してリレーションシップを定義しています。
　　　　各Cafeが1つのAreaに関連付けられています。
　　③ Utility
　　　汎用的なデータを保持するマスタです。
　　　今回は、表示方法の切り替えに利用します。
　　　データの変更で、制御することで、毎回プログラムの変更を行う必要がなくなります。

## 4-2. モデルを有効にする。
　(1) モデル有効化の準備
　　src/mainproject/settings.pyを開き、INSTALLED_APPSに「'app.apps.AppConfig',」を追加します。
　　後術するmigrateコマンドを実行すると、INSTALLED_APPSに記述されている
　　アプリケーションに対し、各アプリケーションに必要なテーブルを作成します。
　　※ app.apps.AppConfigは、src/app/apps.pyのAppConfigクラスを表します。

　　（修正前）

```php
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

　　（修正後）

```php
INSTALLED_APPS = [
    'app.apps.AppConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

　(2) マイグレーションファイルの作成
　　以下のコマンドを実行し、モデルの変更内容が記述されたマイグレーションファイルを作成します。

```php
$ docker-compose run --rm web python manage.py makemigrations app
```

　　下記のようなメッセージが出力され、src/app/migrations/0001_initial.pyが作成されます。
　　※ src/app/migrations/0001_initial.pyは、手動で調整することも可能です。

```php
$ docker-compose run --rm web python manage.py makemigrations app
Starting container.postgres ... done
Migrations for 'app':
  app/migrations/0001_initial.py
    - Create model Area
    - Create model Utility
    - Create model Cafe
```

　(3) テーブルの作成
　　マイグレーションファイルを元にモデルのテーブルを作成する。
　　以下のコマンドを実行します。

```php
$ docker-compose run --rm web python manage.py migrate
```

　　下記のようなメッセージが出力され、テーブルが作成されます。

```php
$ docker-compose run --rm web python manage.py migrate
Starting container.postgres ... done
Operations to perform:
  Apply all migrations: admin, app, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying app.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK
```

　(4) テーブルの確認
　　作成したテーブルをDBeaverというSQLクライアントツールを使って確認します。
　　DBeaverを使ったことがない方は、「[DBeaverのインストールとPostgreSQLへの接続](https://qiita.com/Ryooota/items/755e249f5241d2c964e6)」を参考にして下さい。

　　① DBeaverを起動します。
　　② 新しい接続を作成します。
　　　以下の画面で、PostgreSQLに接続するDatabase、User、Password、Portの入力を行います。
　　　※ 「2-11. ALLOWED_HOSTの設定、DBへの接続設定を行う。」の(2)で設定した内容を入力します。
　　　　<img width="990" alt="スクリーンショット 2020-03-09 0.44.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/e4b9ebed-a0b9-6c3b-deba-7d555fc9d8b8.png">

　　③ テーブルの確認
　　　下記の通り、app_area、app_cafe、app_utilityテーブルが作成されています。
　　　　<img width="1112" alt="スクリーンショット 2020-03-09 0.52.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/e00cba10-c9b6-2bad-f6dc-a0b5e5b9e4ed.png">

　(5) データの登録
　　下記のようなSQLを実行し、初期データを登録して下さい。

```php
insert into app_area (name, create_date) values ('清澄白河', now());
insert into app_area (name, create_date) values ('神保町', now());
insert into app_area (name, create_date) values ('代々木公園', now());
insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('ブルーボトルコーヒー', 'カフェモカはここが一番美味しい。', 'https://bluebottlecoffee.jp/', 'bluebottlecoffee_IMG.jpg', now(), '1');
insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('iki ESPRESSO', 'オセアニアンスタイルのカフェ。フードもコーヒーも美味しい。', 'https://www.ikiespresso.com/', 'ikiespresso_IMG.jpg', now(), '1');
insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('GLITCH COFFEE', 'コーヒーが好きになったきっかけのカフェ。一番好きです。', 'https://glitchcoffee.com/', 'glitchcoffee_IMG.jpg', now(), '2');
insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('DIXANS', 'とてもオシャレなカフェ。デザートが絶品です。', 'http://www.dixans.jp/', 'dixans_IMG.jpg', now(), '2');
insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('Fuglen Tokyo', 'コーヒーがとても美味しいです。代々木公園で遊んだ時は必ず寄ります。', 'https://fuglencoffee.jp/', 'fuglencoffee_IMG.jpg', now(), '3');
commit;
```

# 5. 画面の作成
　Databaseに保持した情報を出力するWebアプリケーションを作成します。
　簡単な一覧画面と詳細画面を作成します。

## 5-1. 一覧画面の作成
　(1) src/appの配下に"templates"ディレクトリを作成し、
　　"templates"ディレクトリ配下に""list.html"を作成します。

```php
$ mkdir -p src/app/templates
$ touch src/app/templates/list.html
```

　(2) src/app/templates/list.htmlを開き、以下の内容で保存します。

```php
{% load static %}

<html lang="ja">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}">
  <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon">
</head>
<body>
<table border="1">
  <thead>
    <tr>
      <th>カフェ</th>
      <th>特徴</th>
      <th>エリア</th>
      <th>サイト</th>
    </tr>
  </thead>
  <tbody>
  {% for cafe in cafe_list %}
    <tr>
      <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td>
      <td>{{ cafe.memo }}</td>
      <td>{{ cafe.area.name }}</td>
      <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td>
    </tr>
  {% endfor %}
  </tbody>
</table>
</body>
</html>
```

## 5-2. 詳細画面の作成
　(1) src/app/templatesの配下に"detail.html"を作成します。

```php
$ touch src/app/templates/detail.html
```

　(2) src/app/templates/detail.htmlを開き、以下の内容で保存します。

```php
{% load static %}

<html lang="ja">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}">
  <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon">
</head>

<body>
<h1>{{ cafe.name }}</h1>
<h2><img src="{% static 'app_image/' %}{{ cafe.image_path }}" alt="{{ cafe.name }}のイメージ" title="{{ cafe.name }}のイメージ" width="384" height="384"></h2>
<h2>特徴：{{ cafe.memo }}</h2>
<h2>エリア：{{ cafe.area.name }}</h2>
<h2>サイト：<a href="{{ cafe.website }}">{{ cafe.website }}</a></h2>
<a href="{% url 'app:list' %}">戻る</a>
</body>
</html>
```

## 5-3. ビューの編集
　3-2.で作成した"Hello world."を出力するファイルを編集します。
　src/app/views.pyを開き、以下の修正を行い保存します。

　　（修正前）

```php
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello world.")
```

　　（修正後）

```php
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Area, Cafe, Utility

class IndexView(generic.ListView):
    template_name = 'list.html'
    context_object_name = 'cafe_list'
    def get_queryset(self):
        """Return the last five published records."""
        return Cafe.objects.order_by('name')[:5]
class DetailView(generic.DetailView):
    model = Cafe
    template_name = 'detail.html'
```

## 5-4. URLの対応付け
　3-3.で作成したURLを対応付けするファイルを編集します。
　src/app/urls.pyを開き、以下の修正を行い保存します。
　※ path()を追加し、新しいviewを app.urls モジュールと結びつけます。

　　（修正前）

```php
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

　　（修正後）

```php
from django.urls import path
from django.contrib.auth.views import LoginView
from . import views
app_name = 'app'
urlpatterns = [
    path('', views.IndexView.as_view(), name='list'),
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),
]
```

## 5-5. 静的ファイルの設置
　(1) appアプリケーションの静的ファイルのディレクトリを作成します。

```php
$ mkdir -p src/app/static/app_css
$ mkdir -p src/app/static/app_image
$ mkdir -p src/app/static/app_js
```

　(2) appアプリケーションのcssを作成します。
　　src/app/static/app_css/style.cssを新規に作成し、以下の内容を記述します。

```php
table {
    background-color: #ffffff;
    border-collapse:  collapse;     /* セルの線を重ねる */
}
th {
    background-color: #000080;
    color: #ffffff;                 /* 文字色指定 */
}
th, td {
    border: solid 1px;              /* 枠線指定 */
    padding: 10px;                  /* 余白指定 */
}
```

　(3) DBに登録したCafeの画像ファイルをsrc/app/static/app_imageに配置します。
　　※ 画像ファイルは何でも良いので用意して下さい。
　　　無くても、画像ファイルが表示されないだけなのでスキップしても問題ありません。
　(4) mainprojectプロジェクトの静的ファイルのディレクトリを作成します。

```php
$ mkdir -p src/mainproject/static/pj_css
$ mkdir -p src/mainproject/static/pj_image
$ mkdir -p src/mainproject/static/pj_js
```
　　
　(5) faviconをsrc/mainproject/static/pj_imageに配置します。
　　※ faviconは何でも良いので用意して下さい。
　　　無くても、faviconが表示されないだけなのでスキップしても問題ありません。
　(6) 静的ファイルの収集を行う。
　　以下のコマンドを実行します。

```php
$ docker-compose run web ./manage.py collectstatic
```

　　下記のようなメッセージが出力され、
　　「2-11. ALLOWED_HOST、DB及び静的ファイルの設定を行う。」で説明した通り、
　　STATICFILES_DIRSで指定した共通の静的ファイルと
　　各アプリケーション配下のstaticディレクトリの静的ファイルを収集します。
　　　→ src/app/static/app_css/style.cssと
　　　　src/app/static/app_imageに配置した画像ファイルと
　　　　src/mainproject/static/pj_imageのfaviconが
　　　　src/staticにコピーされます。

```php
$ docker-compose run web ./manage.py collectstatic
Starting container.postgres ... done

137 static files copied to '/code/static'.
```

## 5-6. 動作確認
　ブラウザを開き、"[http://0.0.0.0:8000/app/](http://0.0.0.0:8000/app/)"にアクセスし、
　一覧画面と詳細画面が下記のように表示されていることを確認します。
　(1) 一覧画面
　　![FireShot Capture 008 -  - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/f62d488a-9080-ddaf-c53c-3c8777784d29.png)

　(2) 詳細画面
　　![FireShot Capture 009 -  - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/c4de1f98-8d13-eee4-ef79-3b494e4c104d.png)

NGINX、uWSGI、PostgreSQLが動作するDocker コンテナで、
DjangoのWebアプリケーションを動作確認するまでの手順が完了です。
Webアプリケーションを作る上で、基本的なことは紹介できたと思いますので、
本投稿が役に立てば幸いです。

# 6. 参考情報（Django Admin）
　本投稿では、DBeaverを使ってデータの登録を行いましたが、Django Adminという
　Djangoに用意されている管理画面を使ってデータを登録することも可能です。

## 6-1. 管理ユーザの作成
　以下のコマンドを実行します。

```php
$ docker-compose run --rm web python manage.py createsuperuser
```

　コマンドプロンプトで以下のように任意の情報を入力します。
　　Username：admin
　　Email address：admin@example.com
　　Password：admin

```php
$ docker-compose run --rm web python manage.py createsuperuser
Starting container.postgres ... done
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: 
Password (again): 
The password is too similar to the username.
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
```

## 6-2. モデルをDjango Adminに登録
　　「4-1. モデルの作成」で作成したモデルをDjango Adminで編集できるようにします。

　　src/app/admin.pyを開き、以下の修正を行い保存します。

　　（修正前）

```php
from django.contrib import admin

# Register your models here.
```

　　（修正後）

```php
from django.contrib import admin

from .models import Area
from .models import Cafe
from .models import Utility

admin.site.register(Area)
admin.site.register(Cafe)
admin.site.register(Utility)
```

## 6-3. 動作確認
　ブラウザを開き、"[http://0.0.0.0:8000/admin/](http://0.0.0.0:8000/admin/)"にアクセスします。
　Django Adminのログイン画面が開くので、
　「6-1. 管理ユーザの作成」で作成した管理ユーザでログインして下さい。
　ログイン後、Area、Cafe、Utilityのモデルの参照、追加、更新が出来ることを確認して下さい。

　(1) ログイン画面
　　![FireShot Capture 006 - Log in - Django site admin - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/828f0493-93f8-ade7-b4fd-50dd41b9d20e.png)

　(2) 管理画面
　　![FireShot Capture 007 - Site administration - Django site admin - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/b7b1c865-a8f6-87f7-678b-f52600f2bc0f.png)
　　
# 7. 参考情報（Bootstrap4）
　Bootstrap4を使用する方法を説明します。
　「2-3. requirements.txtの設定」で説明した通り、
　DjangoでBootstrap4を利用するためのパッケージをインストールしていますので、
　以下の手順を行うことで、Bootstrap4のcss、Javascriptの利用が可能です。

## 7-1. django-bootstrap4の設定
　src/mainproject/settings.pyを開き、以下の修正を行い保存します。

　(1) INSTALLED_APPSにdjango-bootstrap4を追加します。

　　（修正前）

```php
INSTALLED_APPS = [
    'app.apps.AppConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

　　（修正後）

```php
INSTALLED_APPS = [
    'app.apps.AppConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'bootstrap4',
]
```

　(2) TEMPLATESのbuiltinsにbootstrap4.templatetags.bootstrap4を追加します。
　　※ これを追加することで、各htmlに{% load bootstrap4 %}を記述する必要がなくなります。

　　（修正前）

```php
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```

　　（修正後）

```php
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            'builtins':[
                'bootstrap4.templatetags.bootstrap4',
            ],
        },
    },
]
```

## 7-2. Utilityテーブルに制御フラグを追加（おまけ①）
　UtilityテーブルにBootstrap4のcssを利用するかしないかを制御するデータを追加します。
　このデータを使って、一覧画面のhtmlでBootstrap4のcssを読み込むか読み込まないか判別します。
　* 使用しない場合、value列の値を”1”以外に設定します。

```php
insert into app_utility (key, value, create_date) values ('bootstrap_flg', '1', now());
commit;
```

## 7-3. 一覧画面の修正
　(1) src/app/views.pyを開き、以下の修正を行い保存します。（おまけ②）
　　get_context_dataを使って、「7-2. Utilityテーブルに制御フラグを追加」で
　　追加したデータをhtmlで利用できるようにしています。

　　（修正前）

```php
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Area, Cafe, Utility

class IndexView(generic.ListView):
    template_name = 'list.html'
    context_object_name = 'cafe_list'
    def get_queryset(self):
        """Return the last five published records."""
        return Cafe.objects.order_by('name')[:5]
class DetailView(generic.DetailView):
    model = Cafe
    template_name = 'detail.html'
```

　　（修正後）

```php
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Area, Cafe, Utility

class IndexView(generic.ListView):
    template_name = 'list.html'
    context_object_name = 'cafe_list'
    def get_queryset(self):
        """Return the last five published records."""
        return Cafe.objects.order_by('name')[:5]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["bootstrap_flg"] = Utility.objects.all().filter(key='bootstrap_flg').first()
        return context

class DetailView(generic.DetailView):
    model = Cafe
    template_name = 'detail.html'
```

　(2) src/app/templates/list.htmlを開き、以下の修正を行い保存します。
　　bootstrap_flg.value == "1"の時に、{% bootstrap_css %}を読み込むように記述しています。
　　今回、おまけ①②をすることで、テーブルのデータによって、
　　Bootstrap4のcssを読み込むか制御できるように対応しましたが、
　　単純に使用したい場合は、おまけ①②は飛ばし、{% bootstrap_css %}を
　　htmlに追加するだけで使用が可能です。
　　
　　（修正前）

```php
{% load static %}

<html lang="ja">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}">
  <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon">
</head>
<body>
<table border="1">
  <thead>
    <tr>
      <th>カフェ</th>
      <th>特徴</th>
      <th>エリア</th>
      <th>サイト</th>
    </tr>
  </thead>
  <tbody>
  {% for cafe in cafe_list %}
    <tr>
      <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td>
      <td>{{ cafe.memo }}</td>
      <td>{{ cafe.area.name }}</td>
      <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td>
    </tr>
  {% endfor %}
  </tbody>
</table>
</body>
</html>
```

　　（修正後）

```php
{% load static %}

<html lang="ja">
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}">
  {% if bootstrap_flg.value == "1" %}
    {% bootstrap_css %}
  {% endif %}
  <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon">
</head>
<body>
<table border="1">
  <thead>
    <tr>
      <th>カフェ</th>
      <th>特徴</th>
      <th>エリア</th>
      <th>サイト</th>
    </tr>
  </thead>
  <tbody>
  {% for cafe in cafe_list %}
    <tr>
      <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td>
      <td>{{ cafe.memo }}</td>
      <td>{{ cafe.area.name }}</td>
      <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td>
    </tr>
  {% endfor %}
  </tbody>
</table>
</body>
</html>
```

## 7-4. 動作確認
　ブラウザを開き、"[http://0.0.0.0:8000/app/](http://0.0.0.0:8000/app/)"にアクセスし、
　一覧画面にBootstrap4のcssが使用されていることを確認します。

　![FireShot Capture 011 -  - 0.0.0.0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165180/c28e6864-d544-faf3-adec-d246f30b3543.png)

# 参考元
[Compose file version 3 reference](https://docs.docker.com/compose/compose-file/)
[Docker Composeチュートリアル: herokuのPostgresをローカルにさくっとコピーする](https://solutionware.jp/blog/2018/05/23/docker-tutorial-heroku-postgres/)
[Supported tags and respective Dockerfile links](https://hub.docker.com/_/postgres/)
[はじめての Django アプリ作成、その 1](https://docs.djangoproject.com/ja/3.0/intro/tutorial01/)
[SQLのSELECT文を、DjangoのQuerySet APIで書いてみた](https://thinkami.hatenablog.com/entry/2015/09/04/235841)
[Djangoにおけるクラスベース汎用ビューの入門と使い方サンプル](https://qiita.com/felyce/items/7d0187485cad4418c073)