[03] Django アプリの立ち上げもさせる

# はじめに

Docker (docker-compose)と Linux に標準で搭載されているシェルを組み合せて、
下記 [概要](#概要)に挙げた Django 環境を 1コマンドで即座に立ち上げるためのコードとその解説です.

本記事の内容は、Zenn で[書籍](https://zenn.dev/robozushi10/books/3d32504b4b5707)として販売している内容と同一です.
Zenn でも書籍の内容は全て無料公開しています.

次の順でコードを作成してください.
動作するコードが作成できるように可能な限り注意を払って執筆しました.

・[[01] デフォルト環境 runserver + SQLite3 の構築](https://qiita.com/robozushi10/items/fc8e40ad7ecc29131ea3/)
・[[02] SQLite3 を PostgreSQL へ置き換える](https://qiita.com/robozushi10/items/7dc07ab6412b285c75da)
・本記事の内容

# 概要

今回は下図・下表の環境を構築する. (前章からの変更点は🔥である)
具体的には次の通り.
**・セットアップ時にアプリケーションの立ち上げも行う**

## 概念図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/25dfd950-b3b6-7e70-beee-5097cea167a4.png)


## コンテナ内の構成

|項目|値|補足|
|:--|:--|:--|
|Python|バージョン3.8.3|`python:3.8.3-slim-buster` を使う|
|Django|バージョン3.0.7||
|アプリサーバ|runserver|8000番ポートを使用する|
|データベース|PostgreSQL 12.0|ポートは次の通り<br/>・ホスト側「53432」<br/>・コンテナ側「5432」<br/>管理者アカウントは次の通り<br/>・アカウント「`admin`」<br/>・パスワード「`admin`」|
|settings.py||ホスト上の `./web/assets/settings.py` が、<br/>コンテナ内に `/usr/src/app/config/settings.py` <br/>として配置される.|
|Django admin ページ||管理者アカウントは次の通り<br/>・アカウント「`admin`」<br/>・パスワード「`admin`」|
|アプリケーション🔥|アプリ名「shop」🔥|本章で起動させるアプリ🔥|


# セットアップコード

## 全文

前章で作成したコードに対して、本記事の内容を適用してください.

:::message alert
web/assets/settings.py より、「DEBUG = True」のみセットアップできることを確認している.
:::

## ファイル構成

上記 GitHub からコードを取得してきた直後の構成である.
前章から変更が生じたファイルには🏷️を、新規作成については🆕を付与している.

```bash
.
|-- db
|   `-- Dockerfile
|-- docker-compose.yml
|-- setupapp.sh 🏷️........... セットアップスクリプト
`-- web
    |-- Dockerfile
    `-- assets .............. コンテナへコピーされるデータ
        |-- entrypoint.sh
        |-- requirements.txt
        |-- sample 🆕
        |   `-- shop 🆕
        |       |-- admin.py 🆕
        |       `-- models.py 🆕
        `-- settings.py 🏷️
```

## 解説

### setupapp.sh

セットアップスクリプトである. 
前章からの変化点は次の(1)〜(5)の通り.

(1) まず、Django を立ち上げる.
(2) 次に「`manage.py startapp shop`」を実行して、サンプルアプリ「shop」を作成する
(3) 作成された ./app/shop/. に対して、事前に作成済みのコードを配置する
(4) ./config/settings.py を編集して「shop」を有効にする
(5) その後、マイグレーションをする

```diff
--- setupapp.sh.ORIG
+++ setupapp.sh
@@ -48,6 +48,18 @@
 docker-compose exec web bash -c "python manage.py migrate"
 docker-compose exec web bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('admin', 'admin@example.com', 'admin')\" | python manage.py shell"
 
+# アプリ「shop」を作成する
+docker-compose exec web bash -c 'python manage.py startapp shop'
+sudo chmod -R 777 web/app/shop
+# 事前に準備しておいた「shop」のコードを配置する
+sudo cp web/assets/sample/shop/* web/app/shop/.
+# 事前に準備しておいた config/settings.py 内のアプリ「shop」を有効にする
+sudo sed -i "s/#   'shop.apps.ShopConfig'/    'shop.apps.ShopConfig'/g" web/app/config/settings.py
+
+# マイグレーションをして「shop」を反映させる
+docker-compose exec web bash -c 'python manage.py makemigrations'
+docker-compose exec web bash -c 'python manage.py migrate'
+
 # runserver を再起動する
 docker-compose restart web
```

### web/assets/sample/shop/models.py

サンプルアプリ「shop」のコードである.

```python
import uuid
 
from django.db import models
from django.utils import timezone
 
# Create your models here.
 
class Book(models.Model):
  """本モデル"""
  class Meta:
    db_table = 'book'
 
  id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
  title = models.CharField(verbose_name='タイトル', max_length=20)
  price = models.IntegerField(verbose_name='価格', null=True, blank=True)
  created_at = models.DateTimeField(default=timezone.now)
 
  def __str__(self):
    return self.title
```

###  web/assets/sample/shop/admin.py

サンプルアプリ「shop」のコードである.

```python
from django.contrib import admin
from .models import Book
 
class BookModelAdmin(admin.ModelAdmin):
  list_display = ('title', 'price', 'id', 'created_at')
  ordering = ('-created_at' , ) # 降順
  readonly_fields = ('id', 'created_at')
 
admin.site.register(Book, BookModelAdmin)
```


### web/assets/settings.py

前章からの変化点は次の通り.
・全ホストからの接続を許可する記述をした.
・行頭の「#」を解除すれば、すぐに「shop」アプリが動くようにしている.

```diff
--- web/assets/settings.py.ORIG
+++ web/assets/settings.py
@@ -25,7 +25,7 @@
 # SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
 
-ALLOWED_HOSTS = []
+ALLOWED_HOSTS = ['*']
 
 
 # Application definition
@@ -37,6 +37,7 @@
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+#   'shop.apps.ShopConfig',
 ]
 
 MIDDLEWARE = [
```

# セットアップ方法

次の通りコマンドを実行する.

```
$ ./setupapp.sh
```

# セットアップ後

## ファイル構成

```bash
* $ tree . --charset=c -a
.
|-- .setupapp.sh.swp
|-- db
|   |-- Dockerfile
|   |-- data [error opening dir]
|   `-- init
|-- docker-compose.yml
|-- setupapp.sh
`-- web
    |-- Dockerfile
    |-- app
    |   |-- config
    |   |   |-- __init__.py
    |   |   |-- asgi.py
    |   |   |-- settings.py
    |   |   |-- urls.py
    |   |   `-- wsgi.py
    |   |-- db.sqlite3
    |   |-- entrypoint.sh
    |   |-- manage.py
    |   `-- shop
    |       |-- __init__.py
    |       |-- admin.py
    |       |-- apps.py
    |       |-- migrations
    |       |   `-- __init__.py
    |       |-- models.py
    |       |-- tests.py
    |       `-- views.py
    `-- assets
        |-- entrypoint.sh
        |-- requirements.txt
        |-- sample
        |   `-- shop
        |       |-- admin.py
        |       `-- models.py
        `-- settings.py

```

## 動作確認方法

次の点を確認すれば良い.

✔ Dockerコンテナが次のように起動していること

```bash
$ docker-compose ps
    Name                  Command               State            Ports         
-------------------------------------------------------------------------------
mydjango307    /usr/src/app/entrypoint.sh ...   Up      0.0.0.0:8000->8000/tcp 
mypostgres12   docker-entrypoint.sh postgres    Up      0.0.0.0:53432->5432/tcp
```

<br/>

✔ http://localhost:8000 にアクセスして次の画面が表示されること.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/d3ec0713-6dfa-369a-d110-cd8dde62e68f.png)

<br/>

✔ http://localhost:8000/admin にアクセスして、アカウント「`admin`」、パスワード「`admin`」でログインできること.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/ccb608ea-fcd5-aa6a-f555-c8200d7337be.png)

<br/>

✔ http://localhost:8000/admin にログインすると、次のように「shop」が存在していること

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/55f90285-da93-1a52-9e07-f0467b1ad745.png)

<br/>

以上.