Python+Djangoを使ってWebページベースの画面を出力する方法

## 概要

IT技術系のオンラインサロンでペアプロ開発をしているんですが、そのリマインドも兼ねてシェアしようと思います。


### 著者のステータス

フロントエンドエンジニアをしています。
実務では主にJS系のプロジェクトが多いため、Pythonは完全初心者レベルです。
今回の感触としてはDjango独自の規則などもありやや難儀した部分もありますが、routesやループなどの構造・記述方法が実装を通じてわかるようになってきました。

### 開発環境

- Python 3.7.4
- OS：MacOS ver 11.1 (Big Sur)
- エディタ：VS Code

### 参考文献

[Django公式ドキュメント](https://docs.djangoproject.com/ja/3.1/)
[はじめての Django アプリ作成、その 1](https://docs.djangoproject.com/ja/2.0/intro/tutorial01/)
[Djangoのテンプレート機能を使ってみる](https://qiita.com/Ajyarimochi/items/9d64b13321f0779fc986)

## 開発手順

### djangoプロジェクト、アプリの作成

Pythonをインストールした状態でターミナルから開発ディレクトリに移動し、コマンドを入力します。
今回は「todo_site」というプロジェクト名を使うため、下記のようになります。

```
django-admin startproject todo_site
```

さらに下記コマンドで、プロジェクトの中に「todo_app」アプリを作成します。

```
python manage.py startapp todo_app
```

このアプリをプロジェクトで読み込みさせるため、「todo_site」内の設定ファイルでアプリ一覧に該当アプリ名を追加します。

```python:todo_site/settings.py（抜粋）
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'todo_app',     #追加した行
]
```
これで下準備は整いました。

### Webview構造の実装

今回作っているのはこの2ページです。

- インデックスページ(index)：テキスト表示のみ
- ユーザーリストページ(user_list)：外部css読み込み、ループ処理を使ったリスト表示

まず各ページのパス構成を定義するため、パス名やテンプレートを下記のように記述します。

```python:todo_site/todo_app/urls.py
from django.urls import path

from . import views

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

`views`が`views.py`で定義された関数の呼び出しになるので、次に下記のように定義します。

```python:todo_site/todo_app/views.py
from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return HttpResponse("hogehogehoge")

def user_list(request):
    # ユーザーリスト
    context = {
        "items" : [
            {
                "user_name" : "ユーザー1",
                "talk" : "トーク内容",
                "updatetime" : "12:00",
            },
            {
                "user_name" : "ユーザー2",
                "talk" : "トーク内容",
                "updatetime" : "12:00",
            },
            {
                "user_name" : "ユーザー3",
                "talk" : "トーク内容",
                "updatetime" : "12:00",
            },
        ]
    }
    
    return render(request, 'todo_app/user_list.html', context)
```

indexページは単純にテキストのみを表示させるため、`HttpResponse`をインポートしています。
一方でユーザーリストページはカード形式のリストをループで表示させる仕様の実装なので、このファイルではリストの内容を変数`context`で定義し、別のテンプレートファイルを用いてview側に落とし込むという構成をとっています。

### テンプレートを使ったviewの実装

`views.py`内のレンダリング文で記載した`todo_app/user_list.html`の該当テンプレートファイルを作成します。

> djangoのファイル参照の仕様上、複数のindex.htmlファイルを作成した時を考慮してディレクトリを二重にする事が推奨されている。

という部分で個人的に違和感が残ったのですが、ルールに伴い下記のように実装しました。

```python:todo_site/todo_app/templates/todo_app/user_list.html
{% load static %}

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Hello, world!</title>
<link rel="stylesheet" href="{% static 'todo_app/css/user_list.css' %}">
</head>
<body>
    {% for item in items %}
        <div class="item">
            <div class="item-icon">icon</div>
            <div class="item-detail">
                <div class="item-detail-username">{{ item.user_name }}</div>
                <div class="item-detail-talk">{{ item.talk }}</div>
            </div>
            <div class="item-status">
                <div class="item-status-updatetime">{{ item.updatetime }}</div>
            </div>
        </div>
    {% endfor %}
</body>
</html>
```

`views.py`内、`context`関数の中で定義した`items`を読み込み、pythonのループ処理で出力しているという構造です。

さらに

```
load static
```

```
<link rel="stylesheet" href="{% static 'todo_app/css/user_list.css' %}">
```
の2文で下記外部CSSを読み込んでいます。
外部ソース用のディレクトリ「static」を新規作成しています。

```css:todo_site/todo_app/static/todo_app/css/user_list.css（抜粋）
.item {
    height: 60px;
    width: 250px;
    border: solid 1px #000;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0px 5px;
}
```

そして下記コマンドかVSCodeのメニューから実行することでアプリを立ち上げます。

```
python manage.py runserver
```
最終的なビューはこの様になりました。

- インデックスページ

![スクリーンショット 2021-03-12 18.47.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76115/e2841d96-03ed-15cc-0ab8-35490da8b31b.png)

- ユーザーリストページ

![スクリーンショット 2021-03-12 18.47.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76115/5ec113e6-1803-e218-3ba1-18ae114bfe3b.png)