図解Django超入門ハンズオン


![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/7214aea5-4b5c-779f-917e-29230f8aa84a.png)
# はじめに
この記事はPythonとそのWebフレームワークであるDjangoへの入門を目的としたハンズオンの資料です。
Python初心者向けに1.5時間程度のボリュームを想定しています。
図解でDjangoのしくみをイメージしつつ手を動かしてビジュアル的に面白そうなものをフロントエンド技術なしに作ってみたいという方はどうぞお付き合いください。
超入門とあるように文法やフレームワークの機能の詳細な説明は目的としていませんのでご了承ください。

# 作るもの
学びたいことを投稿することができるサービスLearnCloudというものを作っていきます。
![Untitled 14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/afe49e2e-5694-3a26-7d50-712d204fddcf.png)
機能は以下の二つ
- 利用者それぞれが今学びたいと思っている技術やスキルを投稿する機能
- 利用者の投稿をワードクラウドとして表示する機能

ではさっそく始めていきます。

# 1. Pythonのインストール

## 1.1 Linuxへのインストール

### 1.1.1 Pythonのインストールに必要なツールをインストールする。

Linux（Ubuntu）の場合

```bash
sudo apt update

sudo apt install build-essential libbz2-dev libdb-dev \
libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
libncursesw5-dev libsqlite3-dev libssl-dev \
zlib1g-dev uuid-dev tk-dev
```

Linux（CentOS）の場合

```bash
sudo yum groupinstall "development tools"
sudo yum install bzip2-devel gdbm-devel libffi-devel \
  libuuid-devel ncurses-devel openssl-devel readline-devel \
  sqlite-devel tk-devel wget xz-devel zlib-devel
```

Python公式サイトでダウンロードのURLを確認しましょう。

以下のリンク先のFilesのテーブル内の一行目”Gzipped source tarball”のリンクを右クリックし「リンクのアドレスをコピー」をすることでダウンロードURLを確認できます。

[Python Release Python 3.12.3](https://www.python.org/downloads/release/python-3123/)

```bash
# ダウンロードURLからPythonのソースコードを取得する
wget https://www.python.org/ftp/python/3.12.3/Python-3.12.3.tgz

# ダウンロードしたファイルを解凍する
tar -xzvf Python-3.12.3.tgz

# ビルド
cd Python-3.12.3
./configure
make
sudo make install
```

### 1.1.2 インストールの確認

```bash
# 正常にインストールされればバージョンが表示される
python3.12 --version
# (出力) > Python 3.12.3
```

## 1.2 Windowsへのインストール

インストール手順は省略させていただきます。

インストールの確認
```bash
# Winキーで「cmd」と入力し立ち上がったコマンドプロンプトで以下を入力

python --version
```
Windowsの場合、Linuxなどのようにpython3やpython3.12といったコマンドが利用できないかもしれません。
その場合は以降の資料における```python3.12```などのコマンドは```python```に読み替えてください。

## 1.3 Macへのインストール

ここではHomebrewを使用したインストール方法を解説します。

```bash
# ターミナルを開き、Homebrewがインストール済みかどうか確認する
brew -v
# インストールされていればHomebrew 4.2.19などと表示される。

# インストールされていない場合は以下コマンドでインストールする
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# brewを使ってPython3.12をインストール
brew install python@3.12

# インストールできたかどうか確認
python3 --version
# Python 3.12.3
```

## 1.4 参考URL

- **[非公式Pythonダウンロードリンク](https://pythonlinks.python.jp/ja/index.html)**
- **[PythonをLinuxに導入・使用する方法を解説【Ubuntu】](https://www.creativevillage.ne.jp/category/topcreators/web-creator/web-programmer/128983/)**
- [UbuntuでPythonをソースからインストール](https://qiita.com/yniji/items/8e392103a6f2a4152606)
- [python@3.12 - Homebrew Formulae](https://formulae.brew.sh/formula/python@3.12)

---

# 2. 開発環境の準備

## 2.1 VSCodeの準備

省略させていただきます。

## 2.2 Python仮想環境の準備
Python仮想環境を利用することで、プロジェクトごとにライブラリやパッケージのバージョンを独立して管理できるため、環境によるトラブルを未然に防ぐことができます。
今回は標準のvenvというモジュールを使用します。

### 2.2.1 作業ディレクトリの作成

Windowsの場合

1. コマンドプロンプトを開く
    
    windowsキーを押下し「cmd」と入力してエンターキーを押下。
    
2. コマンドプロンプト上で以下のコマンドを入力する
    
    ```powershell
    cd %USERPROFILE%
    mkdir djangohandson
    ```
    

Mac, Linuxの場合、ターミナルを開く

1. ターミナルを起動する
    
    例 F4を押下して「terminal」と入力しエンターキーを押下。
    
2. terminal上で以下を入力する
    
    今回は作業ディレクトリ名を「djangohandson」としましょう！
    
    ```bash
    # ホームディレクトリに移動
    cd ~
    
    # 作業ディレクトリを作成
    mkdir djangohandson
    ```
    

### 2.2.2 仮想環境の作成

2.2.1で作成した作業ディレクトリで以下のコマンドを入力します。

```bash
cd djangohandson

python3.12 -m venv [作成する仮想環境の名前]
```

仮想環境の名前は慣習として.venvが使用されることが多いです。

👉 今回は仮想環境の名前を「.venv」としましょう！

<aside>
💡 隠しファイル

.(ドット)から始まるファイルはOSから隠しファイルと認識され、ファイルエクスプローラー（Finder）上で見えないかもしれない。

その場合は

- Windowsでは
    
    エクスプローラーの「表示」タブで「隠しファイル」に☑️
    
- Macでは ⌘ + ⇧ + .
- Linuxではls -a

とすると隠しファイルも含めて表示できる。

</aside>

<aside>
✏️ pythonコマンドの-mオプション

mはモジュールの意味。-m [モジュール名]と指定して、指定したモジュールの機能を直接実行できる。

さきほどの

```bash
python3.12 -m venv [Python仮想環境名]
```

は、venvモジュールの機能を呼び出している。

</aside>

### 2.2.3 仮想環境を起動する

仮想環境を起動するときは、作業ディレクトリで以下を入力します。

Windowsの場合

```powershell
.venv\Scripts\activate
```

Mac, Linuxの場合

```bash
source .venv/bin/activate
# あるいは
. .venv/in/activate
```

行頭に(.venv)と表示されていれば起動ができています。

### 2.2.4 仮想環境を終了する

Windowsの場合


```powershell
.venv\deactivate
```

Mac, Linuxの場合

```bash
deactivate
```

<aside>
💡 Python仮想環境を削除したいとき

.venvを削除すればOK

</aside>

---

# 3. Djangoプロジェクトの作成

## 3.1 Djangoのインストール

### 3.1.1 pipでDjangoパッケージをインストールする

仮想環境に入っていることを確認して、以下のコマンドを入力します。

```bash
# Djangoをインストールする
pip install django

# インストールされたDjangoのバージョンを確認
python -m django --version
```

<aside>
💡 インストール済みのパッケージ一覧を確認したい

以下のpipコマンドで確認できる。

- pip list
- pip freeze
</aside>

<aside>
💡 pip freezeとrequirements.txとは？

pip freezeは現在のPython環境にインストールされているすべてのパッケージとそのバージョンをリストアップする。
pip freezeした結果をrequirements.txtファイルへ保存することでrequirements.txtに記されたパッケージを一括インストールすることができ、Python環境を簡単に再現することができるようになる。

```bash
# pip freezeコマンドの結果をテキストファイルにリダイレクト(>)する
pip freeze > requirements.txt

# requirements.txtに記載されたパッケージを一括インストールする
# （※requirements.txtが置いてあるディレクトに移動して）
pip install -r requirements.txt
```

たとえば以下のときに便利。

- 別のPCで同じPython環境をつくりたいとき
- プロジェクトメンバに環境を共有したいとき
- CI/CDパイプラインでインストールを自動化したいとき
</aside>

### 3.1.2 Djangoプロジェクトのひな形を生成する

Djangoでは開発するWebサイト, Webサービスを**プロジェクト**という単位で管理します。

![Untitled 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/5ad59b52-da56-1082-3962-f10d5fa1e19e.png)


Djangoをインストールするとdjango-adminコマンドが使用可能になります。

```bash
django-admin startproject [プロジェクト名]
```

👉 今回はプロジェクト名を「learncloudproject」とすることにしましょう！

コマンドを実行すると、以下のようにいくつかのディレクトリやファイルが生成されます。

![Untitled 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/6b5a83b0-42f2-bbe8-764b-6d657efc670f.png)


### 3.1.3 開発サーバーを起動してみる

```bash
# manage.pyが存在するディレクトリで実行する
# runserverするとサーバープログラムがフォアグラウンドで起動する
python manage.py runserver

# 停止するときはctrl + c
```

![Untitled 3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/86757d88-ca64-dae1-a055-e2c66438c724.png)


### 3.1.4 言語設定を変更してみる

settings.pyの以下の部分を変更します。

```python
LANGUAGE_CODE = 'ja'        # default 'en-us'

# ついでにタイムゾーンも変更
TIME_ZONE = 'Asia/Tokyo'    # default 'UTC'
```

開発サーバーを再起動し、言語設定が反映されていることを確認しましょう。
ブラウザに表示された内容が日本語に変わっているはずです。

![Untitled 4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/efbba89f-c5c6-fd42-6c53-2bf718b0d9e3.png)


### 3.1.5 runserverするとコンソール上に気になるメッセージが…

よくみると赤字でこのようなメッセージで表示されています。

![Untitled 5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/0c49520c-86ce-cdd7-539f-f2a8d2b85e0e.png)


Django先生曰く….

> 未適用のマイグレーションが18個あります。admin, auth, contenttypes, sessions のマイグレーションを適用するまで、プロジェクトは正しく動作しないかもしれません。
これらを適用するには 'python manage.py migrate' を実行してください。
> 

ということらしいので、大人しく従っておきましょう。

```bash
# manage.pyがあるディレクトリで以下を実行

# 以下のコマンドでマイグレーションの適用状況を確認することができる。
# [X]が適用済み, [ ]は未適用。
python manage.py showmigrations
# Django先生が教えてくれたコマンド。マイグレーションが実行される。
python manage.py migrate

# マイグレーションが適用されたかどうか、再度確認してみよう。

# サーバー起動時の赤文字が消えたかどうか、確認してみよう。
```

<aside>
💡 （データベースの）マイグレーションとは？

マイグレーションとは移行を意味し、データベースのスキーマを変更することを意味します。たとえば、テーブルにカラムを追加する等。

マイグレーション機能をフレームワークが提供することで以下のようなメリットがあります。

- リリース済みのサービスへ機能追加する際に、既存のデータを失うことなくスキーマ変更することができる。
- データベースのスキーマ変更履歴をマイグレーションファイルとしてアプリケーションのコードと一緒にバージョン管理することが可能になる。
</aside>

# 4. Djangoアプリケーションの作成

DjangoではひとつのWebサービスをひとつのプロジェクトという単位で開発します。

そしてさらにWebサービスの各機能をアプリケーションという単位で分割して開発します。

![Untitled 6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/bab93d7a-0b5c-db28-93b9-3faf39e151f2.png)


## 4.1 アプリケーションの作成

### 4.1.1 アプリケーションのひな型を生成する

```bash
# manage.pyと同じディレクトリ階層で実行する
python manage.py startapp [アプリ名]
```

👉 今回はアプリケーション名を「app」とすることにしましょう！

コマンドを実行するといくつかのディレクトリやファイルが生成されます。

![6f672a8f-19f4-47e5-884e-a5bafbed4b3d.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/01ed8652-1045-d0bb-e1fe-a51881518f08.png)



### 4.1.2 作成したアプリケーションを設定ファイルに追加する

アプリを追加する際は設定ファイルにアプリを追記する必要があります。

settings.pyを開いてlist変数 INSTALLED_APPSにapps.pyで定義されたクラスAppConfigを追加しましょう。

```python
INSTALLED_APPS = [
		(省略),
		'app.apps.AppConfig'   # 追加する！
]
```

これでアプリが利用できるようになります。

## 4.2 Djangoがリクエストを処理する仕組みを知ろう
![Untitled 7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/76015155-1835-7cbb-2831-666d4f6792e9.png)


## 4.3 トップページにアクセスされたときの処理をつくる

### 4.3.1 トップページでHello, Django!と表示してみる

4.2の図を見ると必要なものは2つです。

1. views.pyに”Hello, Django!”というレスポンスを返す関数を定義する。
2. urls.pyにトップページのURL(””)とトップページの処理を定義したview関数の対応関係を記述する。

まず、1です。4.1.1でdjango-adminコマンドによって生成されたviews.pyを開き、以下のように書き換えてみましょう。

```python
# app/views.py

from django.http import HttpResponse
from django.shortcuts import render

def top(request):
    return HttpResponse("Hello, Django!")
```

次に2です。3.1.2でdjango-adminコマンドによって生成されたurls.pyを開き、以下のように書き換えてみましょう。

```python
# learncloudproject/urls.py

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

from app.views import top

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

書き換えたら再び開発サーバーを起動してHello, Django!と表示されることを確認してみましょう。

### 4.3.2 テンプレートの機能を知ろう

![Untitled 8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/020d879d-9186-bd69-00bc-827e7b612761.png)



### 4.3.3 テンプレートを使用してHTMLを返そう

appディレクトリ配下にtemplatesディレクトリを新規作成し、そこにtop.htmlというファイルを作りましょう。

![Untitled 9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/a93f0175-2d5d-5269-473a-24ebc966a206.png)


top.htmlファイルを開き、以下のように書いてみましょう。

```html
<!-- app/templates/top.html -->

<html>
  <head>
    <meta charset="utf-8" />
    <title>LearnCloud</title>
  </head>
  <body>
    <h1>LearnCloudへようこそ！</h1>
    <p>テンプレートをつかってHTMLを配信しています。</p>
  </body>
</html>
```

以下のように表示されていればOKです。

![Untitled 10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/12ea9ff9-f32a-8646-2d0a-05935de654ae.png)


### 4.3.4 テンプレートに変数を埋め込んでみよう

4.3.2で図解したように、テンプレートは受け取ったデータをHTMLに埋め込むことができます。

ここではランダムな整数を生成してテンプレートに埋め込んでみましょう。

```python
# app/views.py

from django.shortcuts import render
import sys # 追加！

def top(request):
    context = {"python_version": sys.version}
    return render(request, "top.html", context)
```

```html
<!-- app/templates/top.html -->

<html>
  <head>
    <meta charset="utf-8" />
    <title>LearnCloud</title>
  </head>
  <body>
    <h1>LearnCloudへようこそ！</h1>
    <p>テンプレートをつかってHTMLを配信しています。</p>
    <p>Pythonのバージョンは{{python_version}}です。</p>
  </body>
</html>

```

以下のように変数をHTMLに埋め込むことができたことを確認できると思います。

![Untitled 11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/8d0f70d7-dd10-4edc-beee-1249f8a35c74.png)


ここまでできたら次はデータベースを扱っていきましょう。

# 5. データベース

## 5.1 モデルの機能を知ろう

![Untitled 12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/d5307efc-12e0-1101-0211-4b5e7ab34039.png)


## 5.2 投稿をデータベースに保存できるようにする

### 5.2.1 Modelクラスの作成

5.1の図から、データベースへ投稿を保存するためにはmodels.pyにModelクラスを定義すればよさそうです。

models.pyを以下のように書いてみよう。

```python
# app/models.py

from django.db import models

class Posts(models.Model):
    id = models.AutoField(primary_key=True)
    skill_name = models.CharField(max_length=128)
```

### 5.2.2 マイグレーションを実行し、テーブルを作成する

書けたら、このモデルに対応するテーブルをデータベースに作成します。

<aside>
💡 まず、データベースを用意しなくてはいけないのでは？と思うかもしれませんが、DjangoをインストールするとデフォルトでSQLite3というデータベースがインストールされるため、今回はそちらを使用します。

settings.pyを見るとDATABASESという項目に、sqlite3の設定がデフォルトで存在していることが確認できます。そのため、データベース接続に関する設定は不要です。

</aside>

では、データベースにテーブルを作成していきます。

Djangoではデータベースのマイグレーション機能があるので、今回の「あたらしくPostsというテーブルを作成する」というスキーマ変更をDjangoに認識してもらい、スキーマ変更を実行してもらうことでデータベースにテーブルを作ることができます。

1. スキーマ変更をDjangoに認識してもらう
    
    ```bash
    # manage.pyがあるディレクトリで実行する
    
    python manage.py makemigrations
    
    # 実行するとapp/migrationsディレクトリにマイグレーションファイルが生成される。
    ```
    
2. スキーマ変更を実行してもらう
    
    ```bash
    # manage.pyがあるディレクトリで実行する
    
    # マイグレーションを実行する
    python manage.py migrate
    
    # マイグレーションが適用されたことを確認する
    python manage.py showmigrations
    ```
    

データベースにテーブルが作成されたことを確認してみましょう。

sqlite3のクライアントツールをインストールすることでsqlite3にログインし、テーブル一覧を確認することができますが、今回は環境に依存しないPythonインタプリタから確認する方法を試してみましょう。

Djangoの機能でPythonインタプリタを起動します。

```bash
# manage.pyがあるディレクトリで実行する

python manage.py shell
```

Pythonインタプリタが起動するので以下のように一行ずつ実行します。

```python
import sqlite3

# データベースファイルに接続
conn = sqlite3.connect('db.sqlite3')

# カーソルオブジェクトを作成
cursor = conn.cursor()

# データベース内のテーブル一覧を取得するSQLクエリを実行
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")

# クエリ結果を取得
tables = cursor.fetchall()

# クエリ結果を表示
for table in tables:
    print(table[0])
    
# 
# 出力例
# 

# django_migrations
# sqlite_sequence
# auth_group_permissions
# auth_user_groups
# auth_user_user_permissions
# django_admin_log
# django_content_type
# auth_permission
# auth_group
# auth_user
# django_session
# app_posts ← 作成できている!!!

# app_postsテーブルのスキーマ情報も確認してみる
cursor.execute(f"PRAGMA table_info(app_posts);")

schema_info = cursor.fetchall()
for column in schema_info:
    print(f"ColumnName: {column[1]}, DataType: {column[2]}, PrimaryKey: {bool(column[5])}")

#
# 出力例
#

# ColumnName: id, DataType: INTEGER, PrimaryKey: True
# ColumnName: skill_name, DataType: varchar(128), PrimaryKey: False

# ↑Modelクラスの定義どおりに作成できていることが確認できる!!!

# コネクションをクローズ
conn.close()

```

### 5.2.3 Pythonシェルからデータを登録してみる

前項で行ったのと同様にPythonシェルを起動します。

Pythonシェル上で以下のように一行ずつ実行します。

```python
from app.models import Posts

# データを一件追加。
Posts.objects.create(skill_name="Python")

# Postsから全件取得し、Pythonの辞書型に変換して表示する
Posts.objects.values()
# 出力結果: <QuerySet [{'id': 1, 'skill_name': 'Python'}]>
```

何件か追加してみましょう。

### 5.2.4 トップページで投稿の一覧を表示する

```python
# app/views.py

from django.shortcuts import render

from app.models import Posts

def top(request):
    posts = Posts.objects.all()
    context = {"posts": posts}
    return render(request, "top.html", context)
```

```html
<html>
  <head>
    <meta charset="utf-8" />
    <title>LearnCloud</title>
  </head>
  <body>
    <h1>LearnCloudへようこそ！</h1>
    <h2>投稿一覧</h2>
    <table>
      <thead>
        <th>ID</th>
        <th>学びたいこと</th>
      </thead>
      <tbody>
        {% for post in posts %}
        <tr>
          <td>{{ post.id }}</td>
          <td>{{ post.skill_name }}</td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </body>
</html>
```

# 6. フォーム

## 6.1 フォームから学びたいことを投稿できるようにする

DjangoではModelFormという機能が用意されていて、Modelクラスに対応したフォームクラスを作成することで、

1. フォームを作成
2. フォームに入力されたデータの受け取り
3. データベースへの格納

という処理の流れを簡単に実装できます。

ここでは詳しく解説しませんが、Djangoの機能で簡単に実装できることを体感してください。

### 6.1.1 ModelFormクラスを作成する

まず、app/配下にforms.pyを作成します。

forms.pyを開いて以下のように書いてください。

```python
# app/forms.py

from django import forms

from app.models import Posts

class PostsForm(forms.ModelForm):
    class Meta:
        model = Posts
        fields = ("skill_name",)
        labels = {"skill_name": "学びたいこと"}
```

続いて、ビューも書き換えます。

```python
# app/views.py

from django.shortcuts import redirect, render
from django.views.decorators.http import require_http_methods

from app.forms import PostsForm
from app.models import Posts

@require_http_methods(["GET", "POST"])
def top(request):
    if request.method == "GET":
        # フォームを生成
        form = PostsForm()
        
        posts = Posts.objects.all()
        context = {
            "form": form, # formをテンプレートに渡す
            "posts": posts,
        }
        return render(request, "top.html", context)
    elif request.method == "POST":
		    # フォームから投稿が送信されたときの処理
        form = PostsForm(request.POST)
        if form.is_valid():
            form.save() # これだけでフォームの情報がデータベースに登録される!
            # 登録された投稿を一覧に反映させるため、再度アクセスさせる
            return redirect(top)
```

最後にテンプレートも書き換えます。

```html
<html>
  <head>
    <meta charset="utf-8" />
    <title>LearnCloud</title>
  </head>
  <body>
    <h1>LearnCloudへようこそ！</h1>
    <h2>学びたいことを投稿しよう！</h2>
    <form method="post">
      {% csrf_token %} {{ form.as_p }}
      <button type="submit">投稿する！</button>
    </form>
    <h2>投稿一覧</h2>
    <table>
      <thead>
        <th>ID</th>
        <th>学びたいこと</th>
      </thead>
      <tbody>
        {% for post in posts %}
        <tr>
          <td>{{ post.id }}</td>
          <td>{{ post.skill_name }}</td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
  </body>
</html>
```

ここまでできたら再度開発サーバーを立ち上げ、実行してみましょう。

フォームが表示され、入力して「投稿する！」ボタンを押下すると一覧に追加されることが確認できると思います。

![Untitled 13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/64a33c64-c017-4eb4-c596-1b478f89f500.png)



# 7. ワードクラウドを表示してみよう

ワードクラウドの作成にはライブラリ[wordcloud](https://github.com/amueller/word_cloud)を使用します。
使い方はとっても簡単です。[シンプルな使用例](https://github.com/amueller/word_cloud/blob/main/examples/simple.py)がリポジトリに公開されていますので確認してみましょう

確認してみると、英文のようなスペース区切りの文字列を用意してWordCloudクラスのgenerateメソッドに渡してあげればワードクラウドができあがります。
画像化する際はWordCloudクラスのto_imageメソッドが用意されておりPillowというライブラリのImageクラスのインスタンスが返ってきます。

さらに、今回生成したワードクラウド画像はHTMLのimgタグにBase64エンコードしたバイナリ形式の文字列として埋め込んで配信するため、上記に加えてPillowのImageクラスをPNG形式に変換しBase64エンコードして文字列に変換するということを行います。

wordcloudをpipでインストール

```bash
# venvに入っていることを確認して実行

pip install wordcloud

# インストールの確認
pip freeze
# 出力結果: wordcloud==1.9.3
```

ビューに処理を追加

```python
# app/views.py

import base64
from io import BytesIO
from django.shortcuts import redirect, render
from django.views.decorators.http import require_http_methods
from wordcloud import WordCloud

from app.forms import PostsForm
from app.models import Posts

@require_http_methods(["GET", "POST"])
def top(request):
    if request.method == "GET":
        # フォームを生成
        form = PostsForm()

        posts = Posts.objects.all()
        wordcloud_image = _generate_wordcloud(posts)
        context = {
            "form": form,
            "posts": posts,
            "wordcloud_image": wordcloud_image,
        }
        return render(request, "top.html", context)
    elif request.method == "POST":
        # フォームから投稿が送信されたときの処理
        form = PostsForm(request.POST)
        if form.is_valid():
            form.save()
            # 登録された投稿を一覧に反映させるため、再度アクセスさせる
            return redirect(top)

def _generate_wordcloud(posts):
    if len(posts) == 0:
        return
    skill_names = [post.skill_name for post in posts]
    text = " ".join(skill_names)
    wordcloud = WordCloud(
        width=800,
        height=400,
        background_color="white",
    ).generate(text)
    return _to_image(wordcloud)

def _to_image(wordcloud):
    # ワードクラウドを画像に変換
    image = wordcloud.to_image()
    buffer = BytesIO()
    image.save(buffer, format="PNG")
    image_png = buffer.getvalue()
    buffer.close()

    # 画像データをBase64エンコードしてバイナリ型にする
    image_base64_binary = base64.b64encode(image_png)
    # UTF-8で文字列にデコードしてHTMLに埋め込める形式にする
    image_base64_string = image_base64_binary.decode("utf-8")
    return image_base64_string
```
完成！
![Untitled 14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3787749/9c61822b-f02d-d12b-b340-4c97a1d01682.png)


---

# 追加セクション

## ワードクラウドを日本語対応にする

デフォルトで使用されるフォントが日本語に対応していないため、正しく表示されていません。

ここではどのOSでも対応できるよう、無償で利用できるIPAフォントをダウンロードして利用することにしましょう。

Djangoアプリappディレクトリ下にstatic/fontsディレクトリを作成しそこにフォントファイルを配置することにします。

### 手順1 IPAフォントのダウンロード

Mac, Linuxの場合

```bash
# Djangoプロジェクト配下に移動した状態で実行する
# 静的ファイル用ディレクトリを作成
mkdir app/static/fonts
# 作成したディレクトリへ移動
cd app/static/fonts
# カレントディレクトリにIPAフォント（ゴシック）をダウンロード
# Macでwgetがなければbrew install wget
wget https://moji.or.jp/wp-content/ipafont/IPAexfont/ipaexg00401.zip
# ダウンロードした圧縮ファイルを解凍する
# unzipがなければインストールしてください。
# (例) Ubuntuならsudo apt install upzip
unzip ipaexg00401.zip
```

Windowsの場合

[IPAフォントのダウンロードリンク](https://moji.or.jp/wp-content/ipafont/IPAexfont/ipaexg00401.zip)からIPAフォントをダウンロードし、解凍して、app/static/fontsフォルダ配下に配置してください。

## 手順2 IPAフォントを使用するようにコードを修正

app/views.pyにosモジュールのインポート追加と_generate_wordcloud関数の内容修正を加えます。

```python
# app/views.py

import os # 追加!!!

def _generate_wordcloud(posts):
    if len(posts) == 0:
        return
    skill_names = [post.skill_name.strip() for post in posts]
    text = " ".join(skill_names)
    # 日本語対応するためにフォントを指定する
    # os.path.join()を使用することで区切り文字の違いなどOSに依存せずパスを生成できる。
    font_path = os.path.join("app", "static", "fonts", "ipaexg00401", "ipaexg.ttf")
    wordcloud = WordCloud(
        font_path=font_path, # font_pathの指定を追加
        width=800,
        height=400,
        background_color="white",
    ).generate(text)
    return _to_image(wordcloud)
```

これで日本語も表示できていると思います。

## WordCloudでスペース区切りの用語も一単語としてみなされるようにしたい

WordCloudクラスのコンストラクタの引数regexpに正規表現を指定することで、1単語としてみなすパターンを自由に指定できる。

たとえば単語の区切り文字を;(セミコロン)に変えて、regexpにセミコロン以外の連続する文字を1単語とみなすようにすれば意図した挙動になる。

（空白区切り以外にするとcollocations=Falseも指定しないとエラーになってしまうため、一緒に指定する。）

```bash
# 区切り文字をセミコロンにする
text = ";".join(skill_names)

# (省略)

wordcloud = WordCloud(
    font_path=font_path,
    width=800,
    height=400,
    background_color="white",
    regexp=r"[^;]+",           # 追加で指定
    collocations=False,        # 追加で指定
).generate(text)
```

が…….

うまく出現頻度が反映されなくなっている気がする…..?

現在、WordCloudクラスのgenerateメソッドを使用しているところを事前に出現頻度を計算してgenerate_from_frequenciesメソッドを使うようにすればうまくいくかも？（未検証…..）

---

# トラブルシューティングQ&A

## Q. VSCodeで「インポート "django.contrib" をソースから解決できませんでした(Pylance[reportMissingModuleSource](https://github.com/microsoft/pyright/blob/main/docs/configuration.md#reportMissingModuleSource))」と表示される。

A. Python仮想環境内のPythonインタプリタが選択できていないor認識されていない可能性があります。

### 手順1 VSCodeにvenvを見つけてもらう

その場合は、VSCode画面左下の歯車アイコンから「設定」を開き、「設定の検索」フォームに「python.venv」と入力します。

表示された「**Python: Venv Folders**」という設定の「項目を追加」ボタンを押下し、作成したPython仮想環境を含むホームディレクトリ上のディレクトリ名を追加しましょう。

### 手順2 適切なPythonインタプリタを選択する

Pythonファイルを開いている場合はVSCode画面右下に現在選択されているPythonインタプリタのバージョンが記載されています。それをクリックすると他のPythonインタプリタが選択できます。

それ以外でもVSCodeのコマンドパレットを開き（Mac: ⌘ + ⇧ + p, Win: ctrl + shift + p）、「>Python: インタープリターを選択」と入力することで同様の操作が可能です。

作成したPython仮想環境のインタプリタを選択するとimport文のエラーが消えると思います。

### 参考URL

- [[VS Code] デフォルトで読み込む venv/virtualenv 環境のパス](https://tekunabe.hatenablog.jp/entry/2018/12/28/vscode_venv_default_rolad)

## Q.  VSCodeで「この大規模なワークスペースをウォッチできません。」と表示される

A. venv内のファイルが監視対象になってしまっている可能性があります。

### 手順 venvをファイル監視から除外するよう設定を追加する

VSCode画面左下の歯車アイコンから「設定」を開き、「設定の検索」フォームに「files.watcher exclude」と入力します。

表示された「**Files: Watcher Exclude**」という設定の「パターンを追加」ボタンを押下し、次のパターン「**/[作成したPython仮想環境名]/**」を追加しましょう。（例） **/.venv/******

### 手順2 それでも警告が表示される場合はOSの制限を調整する

```python
# 現在のOSの設定を確認する
cat /proc/sys/fs/inotify/max_user_watches

sudo vi /etc/sysctl.conf

# 以下の一行を追記する
fs.inotify.max_user_watches=524288
# 設定を適用する
sudo sysctl -p
# 設定が適用されたことを確認する
cat /proc/sys/fs/inotify/max_user_watches
# > 524288
```

### 参考URL

- ["Visual Studio Code is unable to watch for file changes in this large workspace" (error ENOSPC)](https://code.visualstudio.com/docs/setup/linux#_visual-studio-code-is-unable-to-watch-for-file-changes-in-this-large-workspace-error-enospc)