Django 1.11 LTS 主な変更点まとめ

  • 18
    Like
  • 0
    Comment

2017年4月4日、Django 1.11 LTSがリリースされました!
今回のバージョンはLTS(Long Term Support=長期サポート)なので、非LTSバージョンよりサポート期間が長めになっています。サポート期間についての詳細は以下URL「Supported Versions」を確認してください。
https://www.djangoproject.com/download/

それでは、release notesに載っている主な変更点について解説します。

Python2サポートは1.11で最後

1.11はPython2をサポートする最後のバージョンです。次の2.0からはPython3のみサポートになります。
1.11のサポート期限は2020年4月までなので、それまでにはPython3への移行を終える必要があります。1

Deprecating warningsがデフォルトで無効に

Deprecating warningsがデフォルトで無効になりました。Python自体のデフォルトの挙動に合わせたとのことです。
これによって、1.11と1.8両方をサポートするサードパーティアプリケーションを開発する際、警告メッセージを回避するコードを書く必要がなくなりました。
今公開しているサードパーティアプリケーションのサポート対象に次回リリースされる2.0を含める場合、以下がDjango推奨の移行手順です。

  1. バージョン1.11より前のバージョンのサポートを切る。
  2. テストはpython -Wdで実行するようにして、Deprecating warningsが出るようにする。
  3. Deprecating warningsが出ていたら、出ないように修正する。

主な新機能

What’s new in Django 1.11の最初の3機能+その他の個人的に気になる機能について解説します。(それ以外の細かいのは数が多いので省略)

Class-based model indexes

モデルのインデックス指定は今までField.db_index、またはMeta.index_togetherを使っていましたが、1.11からはMeta.indexesにインデックスを表すクラスで指定できるようになりました。
以下が使用例です。

models.py
from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=100)
    price = models.IntegerField()

    class Meta:
        indexes = [
            models.Index(fields=['title', 'author']),
            models.Index(fields=['price'], name='price_idx'),  # インデックスに名前を付けられる
        ]

これによって、B-tree以外のインデックスを指定することが可能になりました。例えば、GinIndexクラスを使うことで、PostgreSQL
の独自機能であるGinインデックスを指定することができます。

Template-based widget rendering

オリジナルのWidgetクラスを作成する際、HTML部分をテンプレートに分けることができるようになりました。
どれだけ便利になったか確認するため、1.8のWidgetクラスはどう書いていたか見てみましょう。

widgets_1_8.py
from django import forms
from django.forms.utils import flatatt
from django.utils.encoding import force_text
from django.utils.html import format_html


class RangeWidget(forms.Widget):
    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        if value is None:
            value = ''
        if value != '':
            final_attrs['value'] = force_text(value)
        return format_html('<input type="range"{}>',
                           flatatt(final_attrs))

今まではHTML部分もrenderメソッドの中に書く必要があり、ちょっと読みにくいです。もっと複雑な仕様になるとスパゲッティコードになる可能性もあります。
次に、1.11での書き方を見てみましょう。

widgets.py
from django import forms


class RangeWidget(forms.Widget):
    template_name = 'forms/widgets/range.html'
range.html
<input type="range" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}{% if widget.value %} value="{{ widget.value }}"{% endif %}>

HTML部分が別ファイルに分かれたので、コードの見通しがかなり良くなりました。テンプレートの書き方はDjangoのソースコードのforms/templates/django/forms/widgets/以下にあるhtmlファイルを見れば参考になります。

Subquery expressions

モデル操作時にサブクエリを使うことができるようになりました。
stackoverflowで「django subquery」 で検索すると結構前から需要がある機能のようですが、ようやく実装されました。
Pythonコードの何がどういうクエリになるか検証するために、ドキュメントのサンプルと同じモデルを作って、以下のような図を作ってみました。
検証にはdjango-debug-toolbardebugsqlshellコマンドを使っています。データベースは公式ドキュメントと違ってSQLiteを使っていますが、投げられるクエリはだいたい同じです。

Subqueryの使用例

公式ドキュメント: https://docs.djangoproject.com/en/1.11/ref/models/expressions/#subquery-expressions
Pythonコード:
Subquery-python.jpg
クエリ:
Subquery-sql.jpg

Existsの使用例

公式ドキュメント: https://docs.djangoproject.com/en/1.11/ref/models/expressions/#exists-subqueries
Pythonコード:
Exists-python.jpg
クエリ:
Exists-sql.jpg

その他

個人的に気になる新機能

The Jinja2 template backend now supports context processors by setting the 'context_processors' option in OPTIONS.」(テンプレートバックエンドがJinja2でもcontext_processorsが使えるようになった)が気になっています。以前、1.8で検証した時には使えなかったのですが、これで気軽にJinja2を採用できそうです。


  1. もっとも、Python2.7のサポート期限は2020年までなので、Djangoでのサポートとは関係なく、そろそろPython3移行を検討するべきです。