勝手に増補「Djangoのツボとコツがゼッタイにわかる本」(第2版)~Herokuの代わりにRenderを使ってみる~

 ハロートレーニングでは「Django3超入門」という書籍をテキストにDjangoを学習したのですが、自己学習でその本をもう一度おさらいした後、この本を新たに購入しました。Djangoは、なかなかにとっつきにくい感じがして、いろんな本で繰り返し学習するのがいいのかなと思ったからです。フレームワークを使うということは実際にソフトウェアを組み上げることに直接繋がっているので、実務でOJTできる環境にない私のような定年退職者には、とりあえずいろんな学習本を試してみる方法がいいように思いました。

 さて、「Djangoのツボとコツがゼッタイにわかる本」(略称「ジャンツボ本」ってことで=^^;=)ですが、系統立てて書かれていて学びやすかったです。WindowsパソコンにWSLでLinuxの仮想環境を作り、VS Codeと連携させながら、さらにPythonの仮想環境を構築して学習環境を作るやり方は、とてもいいです。今後の開発環境をどうしようかなと考えていたこともあり、参考になります。Djangoのプログラミング部分も、「本棚アプリケーション」を作っていく流れで説明されていて、ちょうどこの手のアプリの作成を考えていたので、こちらもばっちりでした。

 で、前回の「Pythonツボコツ本」に続いて、こちらも勝手に注釈というか増補しちゃいます。というのも、この本の最後の方で、Herokuを使ってデプロイする説明があるのですが、このHerokuが2022年11月28日をもって無料プランの提供が廃止されたそうなのです。私のような駆け出しの学習者&低収入定年退職者としては、やはり無料プランで始めたいところです。調べてみたところ、RenderというのがHerokuの代替として無料で使えそうだったので、これも修行じゃ、ということで、トライしてみました。といっても、すでにRenderでのデプロイについてとても詳しく解説されているサイト(Zennというエンジニアのための情報共有コミュニティサイトに、DjangoをRenderにデプロイする方法という記事をはるさんという方が書かれています。)があったので、それを参考にさせてもらいました。ここでは、「ジャンツボ本」の流れで、HerokuをRenderに置き換える際のポイントを中心に書いておこうとおもいます。DjangoのRenderへのデプロイの詳細は、先のサイトをご参照ください。私もほぼ同じ流れで作業してます。

 というわけで、まずは「ジャンツボ本」第6-2章のgithubでのリモートリポジトリ作成とgitのインストールまでは終えておきます。なお、よくわからずにVSCodeからコミットしたら、リポジトリも新たに作成されたのですが、結果的にはこのままではだめでした。=^_^;= このあとにある「6.git環境の再設定」でやり直ししてます。なので、githubのリポジトリ作成は済ませておいてください。 続いて、DjangoをRenderにデプロイする方法を参照しながら、少し進めてみたところ、途中でエラーが発生したこともあり、Renderのドキュメントもあわせて参照しながら以下のやり方で切り抜けました。 

 行った手順は以下のとおりです。「ジャンツボ本」で既に実施済みや作成済みのものは、当然必要ないのでとばしてます。また、既にある内容に合わせて修正する必要もあるので、試行錯誤しながら進めました。こういうところは、パズル感覚で面白いのです。自分の手を動かした者のみがもらえる楽しみです。余談ですが、金はないけど時間はある定年後は、こういう楽しみを積極的に取りに行くのがいいと私は思ってます。

  1. .gitignoreの作成
  2. requirements.txtの作成とパッケージのインストール
  3. setting.pyを修正
  4. スーパーユーザー作成用環境の構築
  5. ビルドスクリプトの作成
  6. git環境の再設定
  7. いざデプロイ
  8. おまけ

 なお、python仮想環境の作成やプロジェクトおよびアプリの作成は、「ジャンツボ本」の中でやっているので何もしてません。

1..gitignoreの作成

 .gitignoreの作成は以下の内容で行いました。

__pycache__
.env
/venv/
/media/*
!/media/.gitkeep
/static/
!/static/.gitkeep
db.sqlite3

.vscode
*.pyc
.DS_Store

2.requirements.txtの作成とパッケージインストール

 requirements.txtは以下の内容で作成し、「pip install -r requirements.txt」コマンドでパッケージをインストールしました。

asgiref==3.7.2
Django==3.2
Pillow==10.1.0
pytz==2023.3
sqlparse==0.4.4
typing_extensions==4.7.1

dj-database-url
dj-static
python-decouple
django-environ
gunicorn
psycopg2-binary
whitenoise

3.setting.pyファイルの修正

 以下のようにsetting.pyファイルに修正(# for Renderでコメントしている箇所)を加えました。whitenoiseってのを使うようなのですが、よくわかってません=^_^;= Djangoでのstatic fileを使いやすくするソフトかなんかなのでしょうか。ただ、このままだとローカルで動かなくなるので、Renderのドキュメントに書いてあるように、if not DEBUG:を挿入して、ローカルでテストしたい場合は、DEBUG = Trueにして動かすようにしました。

from pathlib import Path

# for Render
import os
import environ
from decouple import config
from dj_database_url import parse as dburl

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# for Render
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, ".env"))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-dd__p*&*vi3q__6vt8oog2g56ft#8-hils6e=u(+zl1q)odh1t'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False # デプロイする前には必ず False にします!ローカルでの動作確認時はTrueで

ALLOWED_HOSTS = ['*']


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accounts.apps.AccountsConfig',
    'book.apps.BookConfig',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', # for Render
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

# for Render
default_dburl = "sqlite:///" + str(BASE_DIR / "db.sqlite3")

ROOT_URLCONF = 'bookproject.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / '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',
            ],
        },
    },
]

WSGI_APPLICATION = 'bookproject.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = {
    #'default': {
    #    'ENGINE': 'django.db.backends.sqlite3',
    #    'NAME': BASE_DIR / 'db.sqlite3',
    "default": config("DATABASE_URL", default=default_dburl, cast=dburl), # for Render
    #}
}


# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'
if not DEBUG: # ローカルでの動作確認用に追加
    STATIC_ROOT = str(BASE_DIR / 'staticfiles') # for Render
    STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" # for Render

MEDIA_URL = '/media/'

MEDIA_ROOT = BASE_DIR / 'media'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

LOGIN_REDIRECT_URL = 'index'
LOGOUT_REDIRECT_URL = 'index'

# for Render
SUPERUSER_NAME = env("SUPERUSER_NAME")
SUPERUSER_EMAIL = env("SUPERUSER_EMAIL")
SUPERUSER_PASSWORD = env("SUPERUSER_PASSWORD")

4.スーパーユーザー作成用環境の構築

 Renderへのssh接続は無料版だと使えないらしく、端末でスーパーユーザーの作成ができないので、python経由でスーパーユーザーを作成するコマンドを用意します。setthing.pyの最後にも一部記述がありますが、これに加えて、.envファイルに環境変数を記述し、コマンド発行用のプログラムをsuperuser.pyに書いておきます。

# .envファイル

SUPERUSER_NAME=webadmin
SUPERUSER_EMAIL=webadmin@webadmin.com
SUPERUSER_PASSWORD=*******
# superuser.pyファイル

from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.conf import settings

User = get_user_model()


class Command(BaseCommand):
    def handle(self, *args, **options):
        if not User.objects.filter(username=settings.SUPERUSER_NAME).exists():
            User.objects.create_superuser(
                username=settings.SUPERUSER_NAME,
                email=settings.SUPERUSER_EMAIL,
                password=settings.SUPERUSER_PASSWORD
            )
            print("スーパーユーザー作成")

5.ビルドスクリプトの作成

Render にデプロイするために、build.shファイルと、render.yamlファイルを作成します。

# build.shファイル

#!/usr/bin/env bash
# exit on error
set -o errexit

pip install -r requirements.txt

python manage.py collectstatic --no-input
python manage.py migrate
python manage.py superuser
# render.yamlファイル

databases:
  - name: django_render_db
    region: singapore
    plan: free
    databaseName: django_render_db
    user: django_user

services:
  - type: web
    name: django_render
    env: python
    region: singapore
    buildCommand: './build.sh'
    startCommand: 'gunicorn bookproject.wsgi:application'
    plan: free
    branch: main
    healthCheckPath: /
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: django_render_db
          property: connectionString
      - key: SECRET_KEY
        generateValue: true
      - key: WEB_CONCURRENCY
        value: 4
    autoDeploy: true

6.git環境の再設定

 さて、いよいよデプロイへ、と行きたいところだったのですが、この後エラーが出て試行錯誤したのですが、原因としてgitのルートディレクトリ(って言うんでしょうか?)がmanage.pyのあるディレクトリでなかった(私の場合、ひとつ上にディレクトリになってた)ことでうまくいかなかったわけでした。なので、以下のコマンドをmanage.pyのあるディレクトリで発行してからgitへプッシュし直しました。あと、renderサイトのアカウントも作成しておきます。Renderへのログイン時の認証をgithub経由で行うことで、Renderとgithubの連携がされているようでした。このあたりは、私もよくわかってないので、今後の修練が必要です。ふぅ~

$ git init
$ git add . # ローカルリポジトリにステージング
$ git commit -m "コミットメッセージ(任意の文字列)" # ローカルリポジトリにコミット
$ git status # 状態を確認
$ git remote add origin リモートリポジトリURL(githubに表示されてます) # ローカルリポジトリとリモートリポジトリを紐づけ
$ git push origin main # リモートリポジトリにpush

7.いざデプロイ

 RenderにログインしてBlueprintを発行します。詳細は、DjangoをRenderにデプロイする方法を参照してください。ここにも書かれてますが、初回はデプロイに失敗しますので慌てないようにします。(書いてあるのに、失敗にテンパって原因を探ろうとした私です=^_^;=)なお、失敗後に設定する環境変数PYTHON_VERSIONは、ジャンツボ本の環境である3.10.12にしてます。

 さぁ、ここまでやって再デプロイしてみると…..

デプロイが完了して

サービスが起動!

 そしてRenderにあるURLにアクセスしてみると、登録データはまだありませんが、本棚アプリがRender上で一応動いてるようです。スーパーユーザーも作成されており、ログインできました。管理画面も利用可能です。ま、どっかエラーはあるかもですが、Herokuの代わりにRenderへデプロイするという目的は達成です!やったね、パチパチパチパチ(paizaの霧島さんのノリで =^_^;=)

 とはいえ、定年退職者プログラマーの道はまだまだこれからですねぇ。今回痛感したのは、現役時代は色んなインフラを構築したり運用してきましたが、プログラム開発という視点から見ると、とにかく欠けている知識が多いということ。ひとまず、VS Codeについては手ごろな書籍を購入して学習することにします。また、githubも学習優先度が高いです。その先にはDockerも控えていることでしょうね、きっと。

8.おまけ

 Renderのドキュメントを参照していて気になったものに、poetryがあります。最新のパッケージ管理ツール?のようで気にはなりましたが、DjangoをRenderにデプロイする方法ではpipで書かれていたので、一応以下のようにインストールはしたものの、使いませんでした。結構便利そうなツールなようで、そのうちトライしてみたいです。

$ curl -sSL https://install.python-poetry.org | python3 -
Retrieving Poetry metadata

# Welcome to Poetry!

This will download and install the latest version of Poetry,
a dependency and package manager for Python.

It will add the `poetry` command to Poetry's bin directory, located at:

/home/penpen/.local/bin

You can uninstall at any time by executing this script with the --uninstall option,
and these changes will be reverted.

Installing Poetry (1.7.1): Done

Poetry (1.7.1) is installed now. Great!

To get started you need Poetry's bin directory (/home/penpen/.local/bin) in your `PATH`
environment variable.

Add `export PATH="/home/penpen/.local/bin:$PATH"` to your shell configuration file.

Alternatively, you can call Poetry explicitly with `/home/penpen/.local/bin/poetry`.

You can test that everything is set up by executing:

`poetry --version`

 あと、ALLOWED_HOSTSの設定も気になってます。こちらも情報を集めつつ、ステップアップする必要がありそうです。そろそろ、独自にプログラミングしたいところだったのですが、まだまだ修行が必要なようです。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です