Django 1.11 LTS 主な変更点まとめ

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

それでは、[release notes](https://docs.djangoproject.com/en/1.11/releases/1.11/)に載っている主な変更点について解説します。

## Python2サポートは1.11で最後
[1.11はPython2をサポートする最後のバージョン](https://docs.djangoproject.com/en/1.11/releases/1.11/#python-compatibility)です。次の2.0からはPython3のみサポートになります。
1.11のサポート期限は**2020年4月**までなので、それまでにはPython3への移行を終える必要があります。[^1]
[^1]: もっとも、Python2.7のサポート期限は2020年までなので、Djangoでのサポートとは関係なく、そろそろPython3移行を検討するべきです。

## Deprecating warningsがデフォルトで無効に
[Deprecating warningsがデフォルトで無効](https://docs.djangoproject.com/en/1.11/releases/1.11/#deprecating-warnings-are-no-longer-loud-by-default)になりました。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](https://docs.djangoproject.com/en/1.11/releases/1.11/#what-s-new-in-django-1-11)の最初の3機能+その他の個人的に気になる機能について解説します。（それ以外の細かいのは数が多いので省略）
### [Class-based model indexes](https://docs.djangoproject.com/en/1.11/releases/1.11/#class-based-model-indexes)
モデルのインデックス指定は今まで[Field.db_index](https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.Field.db_index)、または[Meta.index_together](https://docs.djangoproject.com/en/1.11/ref/models/options/#django.db.models.Options.index_together)を使っていましたが、1.11からは[Meta.indexes](https://docs.djangoproject.com/en/1.11/ref/models/options/#django.db.models.Options.indexes)にインデックスを表すクラスで指定できるようになりました。
以下が使用例です。

```django: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](https://docs.djangoproject.com/en/1.11/ref/contrib/postgres/indexes/#django.contrib.postgres.indexes.GinIndex)クラスを使うことで、PostgreSQL
の独自機能である[Ginインデックス](https://www.postgresql.org/docs/current/static/gin.html)を指定することができます。

### [Template-based widget rendering](https://docs.djangoproject.com/en/1.11/releases/1.11/#template-based-widget-rendering)
オリジナルのWidgetクラスを作成する際、HTML部分をテンプレートに分けることができるようになりました。
どれだけ便利になったか確認するため、1.8のWidgetクラスはどう書いていたか見てみましょう。

```django: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での書き方を見てみましょう。

```django:widgets.py
from django import forms


class RangeWidget(forms.Widget):
    template_name = 'forms/widgets/range.html'
```

```jinja: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](https://docs.djangoproject.com/en/1.11/releases/1.11/#subquery-expressions)
モデル操作時にサブクエリを使うことができるようになりました。
stackoverflowで「[django subquery](http://stackoverflow.com/search?q=django+subquery)」　で検索すると結構前から需要がある機能のようですが、ようやく実装されました。
Pythonコードの何がどういうクエリになるか検証するために、ドキュメントのサンプルと同じモデルを作って、以下のような図を作ってみました。
検証には[django-debug-toolbar](https://django-debug-toolbar.readthedocs.io/en/stable/index.html)の[debugsqlshell](https://django-debug-toolbar.readthedocs.io/en/stable/commands.html#debugsqlshell)コマンドを使っています。データベースは公式ドキュメントと違ってSQLiteを使っていますが、投げられるクエリはだいたい同じです。

#### Subqueryの使用例
公式ドキュメント: https://docs.djangoproject.com/en/1.11/ref/models/expressions/#subquery-expressions
Pythonコード:
<img width="1377" alt="Subquery-python.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/7809/b5aeeb5f-d61e-b500-3193-43b158443ec6.jpeg">
クエリ:
<img width="821" alt="Subquery-sql.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/7809/e5857e06-ee90-d7e9-97e4-44599e4418e1.jpeg">

#### Existsの使用例
公式ドキュメント: https://docs.djangoproject.com/en/1.11/ref/models/expressions/#exists-subqueries
Pythonコード:
<img width="1099" alt="Exists-python.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/7809/326e8ec5-4d40-1581-2bff-2edce9e5f88b.jpeg">
クエリ:
<img width="1362" alt="Exists-sql.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/7809/98e17b0f-c8a6-2bd6-ee8f-02f4c8b24357.jpeg">

## その他
### 個人的に気になる新機能
「[The Jinja2 template backend now supports context processors by setting the 'context_processors' option in OPTIONS.](https://docs.djangoproject.com/en/1.11/releases/1.11/#templates)」（テンプレートバックエンドがJinja2でもcontext_processorsが使えるようになった）が気になっています。以前、[1.8で検証した時](http://qiita.com/ryu22e/items/e50f8a3fbd6fe836c1b4)には使えなかったのですが、これで気軽にJinja2を採用できそうです。