前回の記事で環境を構築したアプリですが、動作環境は Python 3.7 & Django 2.1 でした。
Python3.4 & Django1.8な個人アプリを、Python3.7 & Django 2.1 へとアップデートした - メモ的な思考的な
また、ブログには書いていませんでしたが、その後 Python 3.8 系とDjango 2.2 系にバージョンアップしていました。
https://github.com/thinkAmi/dj_ringo_tabetter/commit/236895fb8a7e85a6f46ec7e3555bb400354438f1
今回、WSL2 Ubuntu 環境にて Djangoアプリを最新の Python 3.10 & Django 4.1 へバージョンアップしたため、メモを残します。
目次
環境
- WSL2 Ubuntu 22.04.1 LTS
- 前回の記事 で構築した開発環境
- 現在のバージョン
- Python 3.8.6
- Django 2.2.16
- PostgreSQL 10.6
- アップデートのバージョン
バージョンアップ方針
やること
基本的にはテストがパスして動作すればOKとします。
なお、Djangoの公式ドキュメントによると、Django 4.1系はPython3.8系をサポートしているため、
- まずはDjangoまわりをバージョンアップ
pip-review
で一気にバージョンアップ- 小さなアプリなため、Django3系を飛ばしてもある程度は動くはず...
- 最後にPythonをバージョンアップ
の流れで実施することにします。
なお、バージョンアップする上で問題が発生した場合は、都度対応していきます。
やらないこと
パッケージ管理方法の変更
今は rquirements.txt
で各パッケージのバージョンを管理しています。
将来的には何かしらに移行したいですが、今回はこのままにします。
機能追加
今回はバージョンアップだけにします。
バージョンアップ開始
pip-reviewによるバージョンアップ
前回の記事でDjangoが動くところまで確認しているため、 pip-review
を使って一気にパッケージをバージョンアップします。
jgonggrijp/pip-review: A tool to keep track of your Python package updates.
importlib-metadata のエラーに対応
pip-reviewを実行したところ、 importlib-metadata
のバージョンアップでエラーが出ました。
$ pip-review --auto Collecting atomicwrites==1.4.1 Using cached atomicwrites-1.4.1.tar.gz (14 kB) Preparing metadata (setup.py) ... error error: subprocess-exited-with-error × python setup.py egg_info did not run successfully. │ exit code: 1 ╰─> [25 lines of output] /home/<user>/dev/projects/dj_ringo_tabetter/env/lib/python3.8/site-packages/setuptools/_importlib.py:23: UserWarning: `importlib-metadata` version is incompatible with `setuptools`. This problem is likely to be solved by installing an updated version of `importlib-metadata`. warnings.warn(msg) # Ensure a descriptive message is shown.
同じようなエラーがないかを探したところ、 setuptools
に issue がありました。
[BUG] ImportError when using importlib
with setuptools 60.9.0+ · Issue #3292 · pypa/setuptools
そこで、 requirements.txt
にある importlib-metadata
のバージョンを 0.20
から 0.21
へと修正します。
importlib-metadata==0.21
続いて、 pip install -r requirements.txt
して、現在の環境の importlib-metadata
をバージョンアップします。
バージョンアップ後、 pip-review --auto
したところ、エラーとなることなくバージョンアップが完了しました。
Djangoアプリのテストを実行
PostgreSQLに関するエラーが出て失敗
Djangoアプリには pytest のテストコードがあるため、実行してみました。
すると
$ python -m pytest ... django.db.utils.NotSupportedError: PostgreSQL 11 or later is required (found 10.6).
のような PostgreSQLのバージョンに関するエラーが発生しました。
公式ドキュメントを見ると、Django 4.1系でPostgreSQL 10 系のサポートがなくなったために発生しているようでした。
SupportedDatabaseVersions – Django
PostgreSQLが今のままでは動作しないため、対応を考えました。案としては以下のとおりです。
- PostgreSQLのバージョンを上げる
- 別のDBに移行する
PostgreSQLのバージョンを上げる場合、Djangoアプリの構成を変更せずに済みそうです。
ただ、現在の開発環境だとPostgreSQLはDockerで構築しているため、アップグレード作業が必要そうでした。
アップグレードの方法は色々ありそうでした。
- 【PostgreSQL 9.6→12】pg_upgrade によるアップグレード手順 - Qiita
- docker環境でのpostgreSQL 9.6 => 11 へのアップグレード
- dockerで動かしているPostgreSQL 11をアップグレードしたい
- 開発環境(docker)にてpostgresのdataディレクトリのバージョンアップをお手軽にやる方法(いろんなバージョンでいけるよ) - Qiita
一方、PostgreSQLからSQLiteへ移行する場合、Djangoの機能だけでできそうでした。
DjangoをsqliteからPostgreSQLに切り替えた(dumpdata/loaddata) - [Dd]enzow(ill)? with DB and Python
そこで今回は、 PostgreSQL から SQLite へ移行することにしました。
DBをPostgreSQLからSQLiteへ移行
再掲となりますが、以下の記事を参考に移行を進めます。
DjangoをsqliteからPostgreSQLに切り替えた(dumpdata/loaddata) - [Dd]enzow(ill)? with DB and Python
以下のコマンドを実行し、PostgreSQLのデータをダンプします。
$ python manage.py dumpdata --exclude auth.permission --exclude contenttypes > dump.json
settings.pyをSQLiteの設定へと書き換えます。
DATABASES = { 'default': { 'NAME': os.path.join(BASE_DIR, 'ringo.db'), 'ENGINE': 'django.db.backends.sqlite3', }
データベースのマイグレーションをします。
$ python manage.py migrate
データをロードします。
$ python manage.py loaddata dump.json Installed 794 object(s) from 1 fixture(s)
PyCharm の Database で中身を確認すると、データが移行されていました。
その後、Djangoアプリを起動して確認すると円グラフが表示されました。移行は成功したようです。
PostgreSQLの関数を使っているコードを修正
月別の折れ線グラフ用のデータを作成するために、Djangoアプリでは以下のコードを書いていました。
ここの extra()
で使っている date_part
がPostgreSQLの関数になります。
9.9. 日付/時刻関数と演算子
cls.objects.extra(select={'month': "date_part('month', tweeted_at)::int"}) \ .values('name', 'month') \ .annotate(quantity=models.Count('name')) \ .order_by('name', 'month')
このままではSQLiteで動作しないため、SQLite用の関数に修正します。
SQLiteでは strftime
関数を使うことで、同じような挙動が実現できそうでした。
Date And Time Functions
ただ、上記の公式ドキュメントの例では、strftimeに列名を渡している例が見つかりませんでした。
そこで stackoverflowの記事を探したところ、以下で列名を strftime
を渡しているものがありました。 strftime
の第二引数に列名を '
無しで渡せば良いようです。
strftime function in SQLite does not work - Stack Overflow
そこで、stackoverflowを参考に修正します。
cls.objects.extra(select={'month': "strftime('%m', tweeted_at)"}) \ .values('name', 'month') \ .annotate(quantity=models.Count('name')) \ .order_by('name', 'month')
差し替えた後、Djangoアプリの月別折れ線グラフがエラーにならないか確認しましたが、問題なく表示できました。
ただ、テストコードが落ちるようになりました。戻り値の配列が PostgreSQL と SQLite では異なっているのが原因だったため、テストコードも修正しました。
Djangoの警告に対応
ここまででDjangoは起動できるようになりましたが、 run server したときに警告が出てましたので、対応します。
DEFAULT_AUTO_FIELD の警告に対応
run sever したときに以下の警告が出ていました。
WARNINGS: tweets.LastSearch: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'. HINT: Configure the DEFAULT_AUTO_FIELD setting or the TweetsConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'. tweets.Tweets: (models.W042) Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'. HINT: Configure the DEFAULT_AUTO_FIELD setting or the TweetsConfig.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'. System check identified 2 issues (0 silenced).
この警告を調べたところ、以下の記事がありました。Djagno 3.2の変更によるものでした。
主キーを django.db.models.AutoField
のままにするか django.db.models.BigAutoField
にするか考えましたが、桁数が多いほうが良いだろうと考え django.db.models.BigAutoField
とすることにしました。
Model field reference | Django documentation | Django
そこで、 settings.py
の末尾に以下を追加しました。
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
変更後マイグレーションが必要なので実行します。
# マイグレーションファイルを作成 $ python manage.py makemigrations Migrations for 'tweets': apps/tweets/migrations/0002_alter_lastsearch_id_alter_tweets_id.py - Alter field id on lastsearch - Alter field id on tweets # マイグレーションを適用 $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, tweets Running migrations: Applying auth.0012_alter_user_first_name_max_length... OK Applying tweets.0002_alter_lastsearch_id_alter_tweets_id... OK
Djangoアプリを再起動したところ、警告は出なくなりました。
Python環境を3.10.7へバージョンアップ
前回の記事で pyenv
を使ってPythonを入れていたため、今回も pyenv を使って Python 3.10.7 をインストールします。
Pythonのバージョンアップ
# venvを無効化 (env) $ deactivate # pyenvでPython3.10.7をインストール $ pyenv install 3.10.7 Downloading Python-3.10.7.tar.xz... -> https://www.python.org/ftp/python/3.10.7/Python-3.10.7.tar.xz Installing Python-3.10.7... Installed Python-3.10.7 to /home/<user>/.anyenv/envs/pyenv/versions/3.10.7 # pyenvでバージョン切り替え $ pyenv local 3.10.7 # バージョン確認 $ python --version Python 3.10.7 # venv環境の作成 $ python -m venv env_3_10_7 # 有効化 $ source env_3_10_7/bin/activate
パッケージの再インストール
requirements.txt
からパッケージを再インストールします。
backports.zoneinfo のエラーに対応
再インストールしたところ、以下のエラーが発生しました。
(env_3_10_7) $ pip install -r requirements.txt ... note: This error originates from a subprocess, and is likely not a problem with pip. ERROR: Failed building wheel for backports.zoneinfo Failed to build backports.zoneinfo
requirements.txt
を見ると、たしかに backports.zoneinfo
がありました。
backports.zoneinfo==0.2.1
zoneinfo
モジュールですが、Python 3.9から標準パッケージとして導入されました。
Python 3.9の新機能 - python.jp
そのため、 backports.zoneinfo
は Python 3.9 未満向けのバックポートパッケージと考えられました。
pganssle/zoneinfo: Reference implementation for the proposed standard library module zoneinfo
今回構築するPython3.10系の環境では不要と判断し、 requiremetns.txt
から backports.zoneinfo
の行を削除しました。
再度 pip install -r requirements.txt
すると、エラーにならずにインストールが完了しました。
動作確認
再度
- Djangoアプリの起動
python -m pytest
によるテストの実行
を行い、エラーなく動作していることが確認できました。
これにより、Python 3.10 & Django 4.1 へのバージョンアップが完了しました。
ソースコード
Githubに上げました。
https://github.com/thinkAmi/dj_ringo_tabetter
今回のプルリクはこちら。
https://github.com/thinkAmi/dj_ringo_tabetter/pull/16
なお、Herokuへアップロードしている master
ブランチへはプルリクをマージせず、しばらくは development
ブランチへマージすることにします。