PythonAnywhereでDjangoアプリをデプロイする時の流れとコツ

# はじめに
この記事はwebアプリ初心者が「webにDjangoアプリをデプロイしてみたい！」という思いのもと、PythonAnywhereと格闘した記録を残そうといった趣旨のものになります。

基本的には公式ドキュメントの内容に準拠しておりますので下記ページもご一読ください！
- [Deploying an existing Django project on PythonAnywhere](https://help.pythonanywhere.com/pages/DeployExistingDjangoProject/)
- [How to setup static files in Django](https://help.pythonanywhere.com/pages/DjangoStaticFiles/)


# PythonAnywhereとは
PythonAnywhereはブラウザ上でPythonプログラミングとウェブアプリケーションのホスティングができるクラウドサービスです。
コーディング、実行、デプロイが一か所で可能で、初心者にも使いやすい環境を提供してくれます。(by [生成AI.Claude](https://claude.ai))
- __[PythonAnywhere](https://www.pythonanywhere.com)__


# 前提・環境
- Python 3.10（PythonAnywhere）
- Python 3.11.2（開発環境）
- Django 5.0.7
- PythonAnywhereライブラリは使わない
- データベースはSQLite3
- webアプリは作成済み
- 記事に使った画像のモザイク部はユーザー名

```bash:ディレクトリ構成
.
├── EmoRecord
│   ├── EmoRecord
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── migrations
│   │   ├── models.py
│   │   ├── static
│   │   │   ├── EmoRecord
│   │   │   │   └── style.css
│   │   │   ├── admin
│   │   │   │   ├── css
│   │   │   │   ├── img
│   │   │   │   └── js
│   │   │   └── favicon.ico
│   │   ├── templates
│   │   │   ├── EmoRecord
│   │   │   │   ├── base.html
│   │   │   │   ├── checkPast.html
│   │   │   │   ├── record.html
│   │   │   │   └── recordResult.html
│   │   │   └── login.html
│   │   ├── tests.py
│   │   ├── urls.py
│   │   └── views.py
│   ├── db.sqlite3
│   ├── manage.py
│   ├── mysite
│   │   ├── __init__.py
│   │   ├── asgi.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── requirements.txt
└── README.txt
```


:::note warn
Raspberry Pi 5で開発していたので、開発環境とデプロイ環境でPythonのバージョンが違っています。
デプロイ時に気づいたのですが、なんとか問題なく動きました…
:::



# 1.webアプリ開発〜ファイルの移動
まずはwebアプリを作成しましょう！
(今回、僕はEmoRecordというアプリを作成しました)

作成過程は本題ではないので __割愛！__

次にPythonAnywhereにてアカウントを作成し、「Console」タブからBashの立ち上げを行いましょう。
↓赤枠部をクリックしてください↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/bc8bb5a2-7f70-aba4-9924-741e63168db2.png)

クリックするとPythonAnywhereサーバ内でBashが立ち上がります。
ここで立ち上がるBashは一般的なBashと同じ使い心地なので、使ったことがない方は「Linux コマンド」などで検索してコマンドを入力してみましょう！

次にBashを使って、GitHubからPythonAnywhereサーバ内にwebアプリをコピーします。
このファイル移動に関しては諸兄が記事化してくれているので、__またも割愛！！__
僕は以下の記事にお世話になりました、ありがとうございます！！

- [Djangoで作成したWebアプリケーションを無料で公開する(PythonAnywhere)](https://qiita.com/t-shirayama/items/a8dc8c7946fdea00d8fa)

なお、手順としてGitHubからファイルをcloneしてくるのですが、そのためにはGitHubのアクセストークンを作成して利用する必要があることに注意が必要です。
（下記記事が参考になりました、感謝！）

- [GithubでPersonal access tokens(アクセストークン)を設定してみた](https://dev.classmethod.jp/articles/github-personal-access-tokens/)

また、事前にpip freezeでライブラリをrequirements.txtファイルに書き込んでおくと後で一括インストールする際に便利なのでぜひしておきましょう！



# 2.仮想環境の作成,ライブラリのインストール
次に、先程立ち上げたBash内で下記のコマンドを入力して仮想環境を作成しましょう。
「python3.10」の部分はwebアプリの開発環境で使用したPythonのバージョンに読み替えてください。

```shell
# python3.10の環境下で動作させたい場合
$ mkvirtualenv --python=/usr/bin/python3.10 [仮想環境名]
```

サーバ内で仮想環境を作成する理由は、複数のwebアプリをPythonAnywhereにてデプロイする場合に備え、依存関係コンフリクトが発生しないようにするためと思われます。
仮想環境が無事作成されると、コマンドラインの先頭に([仮想環境名])がつくようになります。

仮想環境を作成できたら、先程作成したrequirements.txtファイルを用いて下記コマンドでライブラリのインストールを行います。

```shell
([仮想環境名])$ pip install -r requirements.txt
```

インストールには少し時間がかかるので気長に待ちましょう。


# 3.webアプリとWSGIファイルの設定
ライブラリのインストールが無事完了したら、Bashは少しの間おやすみです。
次はPythonAnywhereサーバー内の設定ファイルを変更していきましょう。

まずは「Web」タブを開いて、新しいwebアプリの設定を行います。
↓赤枠部をクリックしてください↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/83c93e31-cdfc-5a84-c2f9-b9143b08cd07.png)

すると、以下のような選択画面が出てきますので、「Manual configuration」を選択してください。
（他の選択肢はサーバー内で新しくプロジェクトを一から立ち上げることになります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/f8e36b5a-259e-b722-3c67-72a1f45cdacf.png)

後の選択肢は全て「Yes」や「Next」を選択すれば問題ありません。
Pythonのバージョンも聞かれるので、開発環境で使用したバージョンを答えましょう。

:::note info
ここで僕は「詰んだ」と思いかけましたが、簡単なアプリだったので難を逃れました。怪我の功名。
:::


サーバーに無事プロジェクトを設定できた場合、「Web」タブを開くとwebアプリの環境設定画面が見られるようになります。

↓こんな感じ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/5aad1e61-9f32-31ee-dff2-08e6183ebdf9.png)

続いて、仮想環境名の指定を行います。
「web」タブページ中盤に「Virtualenv」の設定欄があるので、入力欄に先程作成した仮想環境の[仮想環境名]を入力しましょう。
僕は今回"EmoReco-venv"という名前の仮想環境を作成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/56a48b74-ce4d-3563-ebe7-f2f6687fd62d.png)

仮想環境ディレクトリのPathを入れてもいいですが、仮想環境名を入力するだけで勝手に検索＆変換してくれます。ありがたい！

次にPythonAnywhereサーバーのWSGIファイルの設定を行います。
同じく「Web」タブ内の中盤にある __WSGI configuration file__ をDjangoアプリ用に編集していきましょう。

↓赤枠部をクリックしてください↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/7eb7aa6a-6680-0197-87c8-6b1e2632a7c6.png)

ファイルを開くと各フレームワーク用の設定が記述されたコードが出てくるので、以下の「DJANGO」部分のみを残して他の部分は削除してしまいましょう。

```python:/var/www/[ユーザー名]_pythonanywhere_com_wsgi.py
# +++++++++++ DJANGO +++++++++++
# To use your own Django app use code like this:
import os
import sys

# assuming your Django settings file is at '/home/myusername/mysite/mysite/settings.py'
path = '(①)'
if path not in sys.path:
    sys.path.insert(0, path)

os.environ['DJANGO_SETTINGS_MODULE'] = '(②).settings'

## Uncomment the lines below depending on your Django version
###### then, for Django >=1.5:
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
###### or, for older Django <=1.4
#import django.core.handlers.wsgi
#application = django.core.handlers.wsgi.WSGIHandler()
```

削除できたら、コード中の①,②の部分を以下の内容に書き換えましょう！
- ①：manage.pyが存在するディレクトリのパス
　→今回であれば"/home/[ユーザー名]/EmoRecord"
- ②：settings.pyが存在するディレクトリ名
　→今回であれば"mysite"

なお確認の際は「Files」タブから実際にファイルが存在するパスを確認しながら入力すると確実です！

↓赤枠部が"manage.py"が存在するディレクトリのパス↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/0ac4a189-a8f9-aa10-51b9-74de9f1c8524.png)


# 4.データベースの設定, アプリ起動
ここまできたら後少し！
再びBashを立ち上げ、"manage.py"が存在するディレクトリまで移動しましょう。
移動したらおまじないとして、下記コマンドでマイグレーションを行います。

```bash
./manage.py migrate
```

続いて「web」タブにてReloadボタンを押し、設定した内容に間違いがなければ、無事サーバ状のwebアプリが動き出しているはずです…！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/9e35ea7e-2069-01bd-756d-666d330e1a80.png)


# 5.staticファイルの設定
さて、ここまで来て、実はまだ必要な作業が残っています。
現状の立ち上がったwebアプリを確認してもらうと、cssなどの静的ファイルが読み込まれず、かなり質素なUIが表示されているのではないでしょうか？？

これはまだPythonAnywhereサーバ内で静的ファイルのルーティングを行っていないことが原因なので、最後にちゃちゃっと片付けちゃいましょう！

まずは「web」タブを開き、「Static files」の設定を行いましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/9b0688eb-b425-4ca1-4423-7d832e65825c.png)

ここが結構ハマりどころでした。
collectstaticコマンドを使用するなど過去記事や公式リファレンスを参考にアレコレ試したのですが、結果的に以下のように設定すれば良いものかと思われます。
- URL：設定ファイル"setting.py"のSTATIC_URL変数に指定したURL名
- Directory：PythonAnywhereサーバ内の実際に静的ファイルが置かれているディレクトリ名

```python:mysite/setting.py
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/

STATIC_URL = '/static/' # ←ここのURL
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
```
と
```bash
.
├── EmoRecord
│   ├── EmoRecord
│   │   ├── __init__.py
│   │   ├── admin.py
│   │   ├── apps.py
│   │   ├── forms.py
│   │   ├── migrations
│   │   ├── models.py
│   │   ├── static # ←ここのPath

```

さらに、
- アプリごとの静的ファイル格納ディレクトリ
- admin用の静的ファイル格納ディレクトリ
- faviconファイル

という順にディレクトリを並べて配置しないとDjangoアプリ側で静的ファイルの存在を認識してくれなくなる（検索してくれない？）ようなので注意が必要です。

この設定を行うことで、無事静的ファイルが読み込まれ、完全なwebアプリをデプロイすることができます！
お疲れ様でした！


 # まとめ
というわけで今回は、Djangoで作成したアプリをPythonAnywhereでデプロイする時のコツを備忘録的にまとめてみました！
pythonでwebアプリを作成する際、プロトタイプを早めに作りたい時にとても便利ですね:hugging:

筆者はwebアプリど素人なので、「こんな楽な方法があるよ！」を知ってる方がおられましたらぜひコメントお願いします！


# 最後に
本日も乱文でありましたが、ご覧いただきありがとうございました！
これからも組み込みやIoTに関する記事を投稿していく予定ですので、よろしくお願いいたします。:blush: