Python学習チャンネル by PyQ

Pythonのオンライン学習プラットフォームPyQのオフィシャルブログです

Django最新バージョン、Django 4.0を紹介します

こんにちは、PyQサポートです。

2021年12月7日にDjango 4.0がリリースされました。
この記事ではDjango 4.0のアップデート内容から、次の項目についてご紹介します。

  • Redisのキャッシュバックエンドを組込みでサポート
  • zoneinfoがデフォルトのタイムゾーン実装に
  • フォームクラスにテンプレートを指定する属性が追加

他にも多くの変更があります。より詳しく知りたい方はDjango 4.0のリリースノートをお読みください。

Django 4.0 リリースの概要

名称Django
リリースバージョンDjango 4.0
リリース日2021年12月7日
サポートしているPython3.8、3.9、3.10
リリースノートhttps://docs.djangoproject.com/en/4.0/releases/4.0/

※アップデートの際はBackwards Incompatible Changes(後方互換性のない変更)に十分注意してください

Redisのキャッシュバックエンドを組込みでサポート

これまで、DjangoのキャッシュバックエンドとしてRedisを使用するには、サードパーティー製の django-redis を使う必要がありました。

Django 4.0からは、Redisのキャッシュバックエンドが組込みでサポートされます。

これを使うには、RedisのPythonバインディングである redis-py が必要です。
django-redis の場合、 redis-py は依存パッケージとしてインストールされます)

基本的なキャッシュ機能であれば、組込みバックエンドを使えばよいでしょう。

ただし、組込みバックエンドで対応していないRedisの高度な機能を使用したい場合は、 django-redis を使う必要があります。
詳細は https://github.com/jazzband/django-redis/issues/585#issuecomment-1002051473 をご覧ください。

zoneinfoがデフォルトのタイムゾーン実装に

Djangoにはタイムゾーン実装を pytz から zoneinfo移行する計画があります。

その計画の一環として、まずDjango 3.2で zoneinfo がサポートされました。
(ただし、まだデフォルトは pytz でした)

それが今回のDjango 4.0からは、zoneinfo がデフォルトのタイムゾーン実装になり、 pytz は非推奨となりました。

そして、Django 5.0から pytz はDjangoのサポート対象外になります。

zoneinfo はPython 3.9から追加されたモジュールです。
そのため、Python 3.8でDjango 4.0をインストールすると、 zoneinfo の代わりとして backports.zoneinfo が自動的にインストールされます。

なお、 pytzzoneinfo には完全に互換性があるわけではありません。
タイムゾーン設定がUTC以外で pytznormalize()localize() を使っている場合は、個別に確認して対応が必要です。

pytzからzoneinfoへの移行方法

移行方法としては、主に2通り考えられます。

1つ目は、Django 3.2 のままタイムゾーン実装を zoneinfo に移行し、その後Django 4.0にアップグレードする方法です。
まだDjango 3.2を使い続けるのであれば、先行して対応しておくことで、将来の作業を減らせます。

2つ目は、まずDjango 4.0にアップグレードして、その後zoneinfoに移行する方法です。
急いでDjango 4.0にアップグレードする必要がある場合は、こちらの方法になるでしょう。

なお、移行のための措置として、 USE_DEPRECATED_PYTZ が用意されています。
settings.pyUSE_DEPRECATED_PYTZ = True を設定すれば、引き続き pytz を使えます。
ただし、 USE_DEPRECATED_PYTZ はDjango 5.0で削除されます。

移行アシストパッケージ pytz deprecation_shim package

また、移行をアシストするパッケージとして、pytz_deprecation_shim package も存在します。

pytz から zoneinfo への移行では、必要に応じて USE_DEPRECATED_PYTZpytz_deprecation_shim packageを利用するとよいでしょう。

フォームクラスにテンプレートを指定する属性が追加

フォーム関連のクラス(FormsFormsetsErrorList)に、template_name属性が追加されました。

Djangoでは、Djangoテンプレートにフォームクラスのインスタンスを渡し、テンプレート内で {{ form }} のように記述すればフォームを自動生成してくれます。

しかし、自動生成されるフォームの表示が要件に合わない場合、Djangoテンプレートでフォームをカスタマイズしなければなりません。

Django 4.0で追加されたtemplate_name属性にテンプレートを設定すると、{{ form }}と記述した箇所に、指定したテンプレートを使ってフォームが表示されます。

簡単な例を使って説明しましょう。

以下はシンプルなフォームクラスと、それを表示するDjangoテンプレートおよびビュー関数です。
(import文などは省略しています)
なお、比較のために、まだフォームクラスの template_name 属性は使用していません。

forms.py

class MyForm(forms.Form):
    message = forms.CharField(label='Message', max_length=100)
    name = forms.CharField(label='Name', max_length=50)

index.html

<form action="{% url 'myapp:send' %}" method="post">
  {{ form }}
<input type="submit" value="OK">
</form>

views.py

def index(request):
    form = MyForm()
    return render(request, "index.html", {"form": form})

これは、以下のような画面になります。Djangoにより、フォームが自動生成されています。

template_name使用前

次に、フォーム用のテンプレート form_snippet.html を作成し、それをMyFormtemplate属性に設定してみます。

form_snippet.html

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

forms.py

class MyForm(forms.Form):
    template_name = "form_snippet.html"  # 追加
    message = forms.CharField(label='Message', max_length=100)
    name = forms.CharField(label='Name', max_length=50)

これは、以下のような画面になります。template_nameを設定したことで、フォームの表示が変わりました。

template_name使用後

なお、 template_name を使わなくても、以下のように{% include %}を使えば同じように表示されます。

index.html

<form action="{% url 'myapp:send' %}" method="post">
  {% include "form_snippet.html" %}  {# 追加 #}
<input type="submit" value="OK">
</form>

そのため、これまでとあまり変わらないように感じるかもしれませんが、template_nameを使うと以下のメリットがあると考えます。

  1. template_name 属性を指定することで、フォームクラスとフォーム用のテンプレートの対応が分かりやすくなる
  2. {% include xxx %} と書くより {{ form }} と書く方がフォームをレンダリングすることを明確にできる

これまでもテンプレートを使ったフォームのカスタマイズはできましたが、template_name属性を使うと、よりスマートな書き方ができそうですね。

まとめ

Django 4.0では、目立った機能追加はありませんが、細かいところで使いやすさが向上していると感じます。

また、pytz はDjango 5.0で廃止されます。pytz から zoneinfo への切替がDjango 5.0 への移行障壁とならないよう、今のうちに移行計画を立てておきましょう。

Copyright ©2017- BeProud Inc. All rights reserved.