PythonのDjangoで最初のWebページ作り

djangoのコマンドの覚書というかコピペ用台紙です。
何度やっても手順が覚えられなかったので、Qiitaの記事にしながらやっていきます。

自分用としては再現のための、コマンドのコピペテンプレートです。
読んでもらう方には、読むだけでコマンドや画像で動作がイメージできるように意識していきます。

## 前提
Ubuntu20.04。
python3は初めからインストールされています。
pipは途中でインストールされてないよって言われます。
プログラミング初心者の勉強用メモで、たくさんコピペさせていただいていますので、わかってない部分もあります。

クラスベースViewが難しかったため、関数ベースViewで書いてます。

## 仮想環境を用意

Pythonなので仮想環境(pythonライブラリの保存場所)を作ります。
今回はvirtualenvを使います。

```
$ sudo apt install python3-pip
$ pip3 install virtualenv
```

プロジェクトフォルダを作ってライブラリの保存場所を作ります。

```
$ mkdir testsite
$ cd testsite
$ virtualenv virtualenv
```

ライブラリの依存関係を再現できるようにするために、空のrequirement.txtを作っておきます。

```
$ touch requirement.txt
```

ファイル数は必要最小限です。
ディレクトリ構造は以下になります。

```
testsite
├── virtualenv
└── requirements.txt
```

## 仮想環境でインストール

仮想環境のvirtualenvにDjangoを入れていくため、仮想環境を起動させます。
これを忘れてローカル環境に入れて汚してしまうので、このために記事にしたところがあります。

ドット(.)と覚えていつも忘れてしまっていたので、sourceで覚えておきます。
現在のシェル(.)で仮想環境の実行です。


```
$ . virtualenv/bin/activate    # source virtualenv/bin/activate
```
```
(virtualenv)MyName@MyPC:~/testsite$ 
```
となれば仮想環境に入っています。
試しに`pip3 list`をするとライブラリの存在がない状態かと思います。

```
$ pip3 list
```

pipでライブラリをインストールしていきます。
version指定をしないと今はDjango3.X系が入りますが、2.2系で行きます。

```
$ pip3 install Django==2.2.24
```


とりあえず初心者なので、ここで`requirement.txt`に練習をかねて書き込みます。

```
$ pip3 freeze -l > requirement.txt
```

```
$ vi requirement.txt
```

```text:requirement.txt
Django==2.2.24
pytz==2021.1
sqlparse==0.4.1

```

## プロジェクトの作成
プロジェクトフォルダは最初に作ってありますので、そこにDjangoのコードを生成します。
公式チュートリアルの作成方法では、プロジェクトフォルダ名と設定ディレクトリ名が同じになってしまいわかりずらいため、わかりやすい構成として、設定ディレクトリを`config`とする構成が用いられているようです
。
`config`という名前で設定ディレクトリを作成、__現在のディレクトリ(.)__をプロジェクトフォルダとするオプションで作成します。

```
$ django-admin startproject config .
```
configフォルダとmanage.pyができました。

```
testsite
├── config
├── virtualenv
├── manage.py
└── requirements.txt
```

これでひとまずは起動できますので、初心者的にはここで起動しておきます、、、

```
$ python3 manage.py runserver
```
が、ターミナル上で、
![サーバー起動テスト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285882/a03c057e-3be9-fa9e-019f-4d5369dea415.png)
と、赤文字でエラーっぽいものが出ます。指示には従いません。
Django公式推奨通り、カスタムユーザーを作ってから`migrate`しますので、起動確認で喜んでみただけです。

![Django起動画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285882/2b32ea45-2f03-9958-09a4-0ef6b44f353d.png)

英語なので日本語に変えます。

```python:config/settings.py
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
```

## 認証・認可機能の作成
Djangoでは__使わなくても__面倒でも先にカスタムユーザーを作ることが推奨されているようです。
Pythonというと__まず仮想環境を作らされる__お作法と近いかもしれません。
カスタムユーザーを作るとなると認可認証アプリを作ることにります。
便利な管理画面が最初からついてくることが原因で、あとから変なエラーがでたりするそうです。

もっと簡単っぽいやり方を紹介してくれている人もいます
[カスタムユーザーモデルを作成する](https://houdoukyokucho.com/2020/06/03/post-1155/)
今回は面倒そうでしたが、準公式とも言えそうなLearnDjango.comから拝借しました
[Django Best Practices: Custom User Model](https://learndjango.com/tutorials/django-custom-user-model)

### manage.pyでapp作成
Djangoでは機能をappという単位で呼ぶそうです。
標準の認証認可の機能を上書きするための、認証認可の機能を`accounts`という名前で作っていきます。

```
$ python3 manage.py startapp accounts
```
### プロジェクト設定ファイルへ読み込み
settings.pyのINSTALLED_APPSの末尾に、自分が作成したaccountsアプリを読み込ませる設定を書きます。
startappでできたファイルの中にapps.pyがあり、その中でクラスが定義されているので、そのクラスの読み込みです。

```python:accounts/apps.py
from django.apps import AppConfig


class AccountsConfig(AppConfig):
    name = 'accounts'
```
↓このクラスを書き写して追加

```python:config/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # manage.pyで作成した認証認可アプリ
    'accounts.apps.AccountsConfig',  # 追加
]
```
上から順番に読まれるそうなので、自分のアプリ(機能)は一番下に書くのがいいのかなぁと思っています。

### カスタムユーザーの作成
作成はしますが、何もしません。passします；

```python:accounts.models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    pass

    def __str__(self):
        return self.username
```

認証ユーザーの上書き
startappで作ったaccountsのCustomUserクラスはmodels.pyにありますが、accounts.models.CustomUserとは書かないことに注意です。
settings.pyの末尾にでもコメントを添えて書き加えます。

```python:config/settings.py
# 管理画面認証ユーザーの設定
AUTH_USER_MODEL = 'accounts.CustomUser'
```

### ユーザー追加、登録フォームの上書き
管理画面とかで使われてるそうなので、怖いので従います。

```
$ touch accounts/forms.py
```

```python:accounts/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class CustomUserCreationForm(UserCreationForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email')

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = CustomUser
        fields = ('username', 'email')
```

### 管理画面への登録
モデル自体とフォームの登録と、画面表示の設定です。

```python:accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin

from .forms import CustomUserCreationForm, CustomUserChangeForm
from .models import CustomUser

class CustomUserAdmin(UserAdmin):
    add_form = CustomUserCreationForm
    form = CustomUserChangeForm
    model = CustomUser
    list_display = ['email', 'username',]

admin.site.register(CustomUser, CustomUserAdmin)
```

### データベースへの登録
```
$ python3 manage.py makemigrations accounts
$ python3 manage.py migrate
```

やっとmigrateできました。
migrateをしたので、db.sqlite3が作成されていますので、見てみます。

```
testsite
├── accounts
├── config
├── db.sqlite3
├── manage.py
├── requirements.txt
└── virtualenv
```
少しずつファイルが増えてきました。

## ページ表示機能の作成

ここでやっと本題の最初のWebページ作りになります。
複数形だとかapp.appsになってややこしいだとか命名規則とかわかりませんが、appで作ります。
### startapp
```
$ python3 manage.py startapp app
```

```
testsite
├── accounts
├── app
├── config
├── db.sqlite3
├── manage.py
├── requirements.txt
└── virtualenv
```

```python:config/settings.py
INSTALLED_APPS = [
    ...,
    # manage.pyで作成した認証認可app
    'accounts.apps.AccountsConfig',   
    # manage.pyで作成したページ表示app # 追加
    'app.apps.AppConfig',          # 追加
]
```

### 設定
MVCと同じようにDjangoのMTVモデルでも、1つのファイルでは表示されません。
3つのファイルをいじってページを作成していきます。
URLにアクセスされたら → サーバー処理 → HTMLを返す順序でユーザーに表示されます。
`urls.py` → `views.py` → `HTMLファイル`の順で作っていくのがわかりやすいかもしれません。

が、セッティングがまだ終わっていません。

#### テンプレートの読み込み先の指定
ディレクトリ直下に`templates`というディレクトリを作ります。

```
$ mkdir templates
```

`settings.py`の`TEMPLATES`の`DIRS`リストに今作ったテンプレートの場所を教えます。

```python:config/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, "templates")],  # ディレクトリ直下のtemplatesフォルダ
        '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',
            ],
        },
    },
]
```
#### 設定ファイルへのルーティング
アクセスはconfig.pyのurls.pyにしか来ない設定になっています。
これからappの中にurls.pyを作りますので、先に読み込ませる設定だけ書きます。

```config/settings.py
from django.contrib import admin
from django.urls import path
from django.urls import include  # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(app.urls)),  # rootパスにmanage.pyで作成したapp内のurls.pyをincludeする。
]
```
urls.pyは今から作ります。

#### urls.pyの作成
includeするurlsを作って書いていきます。

```
touch app/urls.py
```
```python:app/urls.py
urlpatterns = []
```
これで、空のままですが、読み込み設定はできました。

### ページ作成
3つのファイルを一気に作ってHTMLを表示していきます。
先ほどの空の`urls`に`views`の`top関数`を見るよう指示していきます。

```python:app/urls.py
from django.urls import path
from app import views

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

`views`には`top関数`を用意します。
`top関数`は`templates内のapp/top.html`を参照します。

```python:app/views.py
from django.shortcuts import render

def top(request):
    context = {}
    return render(request, 'app/top.html', context)
```

```
mkdir templates/app
touch templates/app/top.html 
```

```python:templates/app/top.html
<h1>top.htmlのページ</h1>
```
![DjangoTopPage2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285882/c665de1a-105f-0f38-1d2a-eb5700dba072.png)
表示できました。

BootStrapを導入してきれいなトップページを作ってみたいと思います。

## BootStrapの導入

```
$ pip3 install django-bootstrap4
```

```python:config/settings.py
INSTALLED_APPS = [
    ..., 
    # Bootstrapの導入
    'bootstrap4',
]

TEMPLATES = [
    {
        ...
        'OPTIONS': {
            'context_processors': [
                ...,
            ],
            # Bootstrapの導入                            # 追加
            'builtins': [                               # 追加
                'bootstrap4.templatetags.bootstrap4'],  # 追加
        },
    },
]
```

```html:template/app/top.html
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}
<div class="jumbotron">
  <h1 class="display-4">Hello, world!</h1>
  <p class="lead">This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
  <hr class="my-4">
  <p>It uses utility classes for typography and spacing to space content out within the larger container.</p>
  <p class="lead">
    <a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a>
  </p>
</div>
```
![DjangoTopPage3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285882/2c56f598-671b-d6c4-03e7-2c060801d9fe.png)
実際コードがどのように変換されるかと言うと、

```html:localhost
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" rel="stylesheet">
<script crossorigin="anonymous" integrity="sha384-ZvpUoO/+PpLXR1lu4jmpXWu80pZlYUAfxl5NsBMWOEPSjUn/6Z/hRTt8+pR6L4N2" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script crossorigin="anonymous" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
```

CDNで取り込まれているだけでした。

とりあえずこれも依存関係として保存して

```
$ pip3 freeze -l > requirements.txt
$ cat requirements.txt 
```
```
beautifulsoup4==4.9.3
Django==2.2.24
django-bootstrap4==3.0.1
pytz==2021.1
soupsieve==2.2.1
sqlparse==0.4.1
``` 

## 参考にしたもの
https://learndjango.com/tutorials/django-custom-user-model