RevComm Tech Blog

コミュニケーションを再発明し 人が人を想う社会を創る

Django 4.2LTSがリリースされたので早速アップグレードした話

はじめに

バックエンドエンジニアの小門 照太です。 RevCommの主要製品であるAI搭載型IP電話「MiiTel」において、バックエンドAPIフレームワークの一つとしてDjangoを利用しています。

さて、4/3(月) にDjango 4.2がリリースされました。
Django 4.2 released

これはLTS(Long-Term Support)バージョンであり、3.2 LTSから2年ぶりのリリースです。
サービスの継続開発と運用においてフレームワークのバージョンアップに追従することは、機能の追加や強化そしてセキュリティ面で重要です。

この度RevCommで運用しているサービスにおいてDjango 4.2へアップグレード(本番環境へ適用)しました。
今回の対応で実施した手順や必要となったソースコード修正の内容をご紹介します。

事前準備

本対応に際して事前にベータ版を使って検証を行いました。
Django 4.1から4.2正式リリースまでの間に下記のバージョンがリリースされており、事前検証や準備をするための期間が設けられています。

本対応では検証バージョンに4.2b1を利用しました。検証用にブランチを作成し、その中でバージョンをアップグレードしてアプリケーションを起動させました。
いくつかエラーが出たため、修正した内容を後述します。

修正内容

django.conf.urls.url

django.conf.urls.url()関数がver4で廃止になりました。 例えば urls.pyにおいて下記のような修正です。

- from django.conf.urls import url
+ from django.urls import path

urlpatterns = [
-    url(r"~~~", ...)
+    path(r"~~~", ...)
]

なおdjango.conf.urls.url()はver3.1から非推奨(Deprecated)とされています。

django.urls functions for use in URLconfs

Deprecated since version 3.1: Alias of django.urls.re_path() for backwards compatibility.

またurls()からpath()への修正後に警告が出るようになりました。

$ python manage.py check
System check identified some issues:

WARNINGS:
?: (2_0.W001) Your URL pattern '^path/to/view$' has a route that contains '(?P<', begins with a '^', or ends with a '$'. This was likely an oversight when migrating to django.urls.path().

System check identified 1 issue (0 silenced).

これは path() のURLパターンに^$といった正規表現を含んでいたことが原因でした。 URLパターンに正規表現が必要な場合のためには django.urls.re_path() 関数が用意されています。

URLパターンから ^$ を削除して警告を解消しました。

urlpattenrs = [
-    path(“^path/to/view/$”, …),
+    path(“path/to/view/”, …),
]

django.utils.translation.ugettext_lazy

上記と同様に Deprecated な関数への対応です。 django.utils.translation.ugettext_laztからgettext_lazyを使用するようにします。

- from django.utils.translation import ugettext_lazy as _
+ from django.utils.translation import gettext_lazy as _

こちらもDjango 3.0のRelease noteに非推奨となる旨が記載されています。
Django 3.0 release notes

django.utils.translation.ugettext(), ugettext_lazy(), ugettext_noop(), ungettext(), and ungettext_lazy() are deprecated in favor of the functions that they’re aliases for:

Djangoドキュメントにおいて、関数/メソッドの deprecation はマイナーバージョンも含めた各Release noteで確認することができます。
しかし、メジャーバージョン毎の大まかなアップデートをRelease noteで俯瞰することは難しいです。

全てのバージョン毎の Deprecated あるいは廃止される関数/メソッドの情報を知るにはRelease noteではなく「Django Deprecation Timeline」を確認すると良いでしょう。

Psycopg 3

PostgreSQL 向けのデータベースドライバーである psycopg ver3 がサポートされました。 これはDjango 4.2 リリースノート「What’s new in Django 4.2」の1番目に記述されています。

Psycopg ver3 では非同期操作のサポートを含む機能強化が行われています。

Differences from psycopg2

従来はPsycopg ver2でしたが、将来的にver2はDeprecatedとなるため今回で対応しました。 ドキュメントの手順に従ってPsycopg ver2からver3にアップグレードします。 ※パッケージマネージャーとしてPoetryを使用している例

$ poetry remove psycopg2-binary
$ poetry add "psycopg[binary,pool]"

また設定モジュール(settings.py)の記述もDjangoドキュメントに従い修正します。 Settings | Django documentation | Django

DATABASES = {
    "default": {
-        "ENGINE": "django.db.backends.postgresql_psycopg2",
+        "ENGINE": "django.db.backends.postgresql",
        ...

動作確認

上述までの修正手順でローカルでエラーが出ないこと、およびCIの成功を以って検証環境への反映を行いました。

$ python manage.py check
System check identified no issues (0 silenced).
$ python manage.py test
...
----------------------------------------------------------------------
Ran 435 tests in 19.712s

OK
Destroying test database for alias 'default' ('myapp')...

そして検証環境で一定期間稼働させて不具合が顕在化しないことを確認して、最終的なリリース判定として本番環境へ反映しました。

Django 4.2LTSが4/3(月)にリリースされて1週間ほどで本番環境にアップグレード適用を完了させました。 今回の迅速な対応を可能にした要素として下記の点が挙げられます。

  • Djangoのリリーススケジュールをウォッチして計画に組み込んでいた
  • 事前準備としてベータ版を適用して検証を行った
  • サービスの正常性を担保するためのCIを整備していた

今後に向けて

以上までの内容に加えてDjango ver4のアップデートに伴う修正の余地はまだまだあります。

今後対応していきたい箇所を合わせて例示してみます。

非同期サポートの強化

Django 4.1でORMによるクエリ実行に非同期サポートが導入されました。
Asynchronous queries

SQLクエリを発行する全ての QuerySet メソッドの接頭辞にaを付けたものが追加されています。

async for author in Author.objects.filter(name__startswith="A"):
    # afirst(), not first()
    book = await author.books.afirst()

データベースに対する操作はIOバウンドであるため、ノンブロッキング処理に切り替える効果が大きいはずですので、検証しつつ徐々に導入していきたいと考えています。

Redis用バックエンドクラスの提供

Django 4.0より、キャッシュ用途でRedisを利用する際のバックエンドクラスをDjangoがネイティブで提供するようになりました。

Django 4.0 release notes

Redis cache backend The new django.core.cache.backends.redis.RedisCache cache backend provides built-in support for caching with Redis.

これにより、DjangoとRedisを統合するためのdjango-redisを利用する必要がなくなります。

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
    }
}

まとめ

Django 4.2LTSのリリースに伴いアップグレードを実施した事例をご紹介しました。
アップグレードして終わりではなく、「今後に向けて」のように対応の余地はまだまだたくさんあるので運用を継続していくことが最も重要であると考えています。

RevCommでは一緒に働いてくださるエンジニアを募集しています。 「コミュニケーションを再発明し人が人を想う社会を創る」というミッションの元、プロダクトの安定稼働を支えるために様々な取り組みをしていますので、ぜひご応募ください。

hrmos.co