Django 2.0の変更点について


この記事は [Django Advent Calendar 2017](https://qiita.com/advent-calendar/2017/django) 2日目の記事です。

9年ぶりとなるメジャーバージョンアップとなるDjango 2.0について紹介していきます。

Django 2.0では、Python2系のサポートは打ち切られて、Python3.4/3.5/3.6のみをサポートすることとなります。

## Django 2.0 Roadmap

https://code.djangoproject.com/wiki/Version2.0Roadmap

| Date | Process |
|:-----------|:------------|
| 2017/09/18 | Django 2.0 alpha; feature freeze. |
| 2017/10/16 | Django 2.0 beta; non-release blocking bug fix freeze. |
| 2017/11/15 | Django 2.0 RC 1; translation string freeze. |
| 2+ weeks after RC1 | Django 2.0 final (or RC 2, if needed). |


現時点ではRC 1がリリースされています。
https://pypi.python.org/pypi/Django/2.0rc1

順調にいけば、12月中にはDjango2.0の正式リリースが発表されるでしょう。

### 追記

この記事を書いた数時間後の12/2に無事リリースされました！ :tada:
[Django 2.0 released | Weblog | Django](https://www.djangoproject.com/weblog/2017/dec/02/django-20-released/)

## 新機能
### シンプルなURLルーティング構文

従来の正規表現を用いた書き方(`django.conf.urls.url()`)に加えて、新たにシンプルな書き方(`django.urls.path()`)ができるようになりました。

```py
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
```

```py
path('articles/<int:year>/', views.year_archive),
```

### モバイルフレンドリーなcontrib.admin

admin画面がレスポンシブ対応になりました。

#### Django 1.1

![スクリーンショット 2017-12-02 09.22.16.png](https://qiita-image-store.s3.amazonaws.com/0/19111/bc58ab0f-011a-382d-e1d6-7c3dac67116a.png "スクリーンショット 2017-12-02 09.22.16.png")

#### Django 2.0


![スクリーンショット 2017-12-02 09.16.24.png](https://qiita-image-store.s3.amazonaws.com/0/19111/8aedf174-831d-359a-0518-dd12b1cf126e.png "スクリーンショット 2017-12-02 09.16.24.png")
### Window表記
* OVER句を追加できるWindow表記が導入されました。

### 細かい機能群

`runserver`で立ち上がるサーバーがHTTP 1.1に対応するなど、他にも細かい新機能があります。ドキュメントをざっと眺めておくとよいでしょう。

https://docs.djangoproject.com/en/2.0/releases/2.0/#minor-features

## 後方互換性がない変更

* I/Oまわりを除いて、bytestringsは使用不可
	* Python2系のサポート切るため
* DBバックエンドAPIの変更
	* サードパーティのDBバックエンドで必要となる変更点
	* https://docs.djangoproject.com/en/2.0/releases/2.0/#database-backend-api
* Oracle 11.2をサポート外
	* Django1.11でサポートしていく
* MySQLの分離レベルのデフォルト設定を`repeatable read`から`read committed`に変更
* `AbstractUser.last_name`のmax_lengthが150文字までに増加
	* `AbstractUser`を使っている場合は、新たにmigrationを適用する必要あり
* スライス後の、`QuerySet.reverse()``QuerySet.last()`は使用禁止

```
>>> Model.objects.all()[:2].reverse()
Traceback (most recent call last):
...
TypeError: Cannot reverse a query once a slice has been taken.
```

* `Form fields`への位置引数を禁止
    * :no_good: `forms.IntegerField(25, 10)`
    * :ok_woman: `forms.IntegerField(max_value=25, min_value=10)`
* `call_command`のオプション指定方法に、`parser.add_argument` 加えて `stealth_options` という簡単な設定が可能に
    * 以下は`flush`commandの実装を一部抜粋したもの

```py
class Command(BaseCommand):
    help = (
        'Removes ALL DATA from the database, including data added during '
        'migrations. Does not achieve a "fresh install" state.'
    )
    stealth_options = ('reset_sequences', 'allow_cascade', 'inhibit_post_migrate')

    def add_arguments(self, parser):
        parser.add_argument(
            '--noinput', '--no-input', action='store_false', dest='interactive',
            help='Tells Django to NOT prompt the user for input of any kind.',
        )
        parser.add_argument(
            '--database', action='store', dest='database', default=DEFAULT_DB_ALIAS,
            help='Nominates a database to flush. Defaults to the "default" database.',
        )

    def handle(self, **options):
        database = options['database']
        connection = connections[database]
        verbosity = options['verbosity']
        interactive = options['interactive']
        # The following are stealth options used by Django's internals.
        reset_sequences = options.get('reset_sequences', True)
        allow_cascade = options.get('allow_cascade', False)
        inhibit_post_migrate = options.get('inhibit_post_migrate', False)
```

* `models.Index`への位置引数を禁止
    * :no_good: `models.Index(['headline', '-pub_date'], 'index_name')`
    * :ok_woman: `models.Index(fields=['headline', '-pub_date'], name='index_name')`
* 外部キー制約をSQLiteでもできるように
* その他
	* https://docs.djangoproject.com/en/2.0/releases/2.0/#miscellaneous

## 非推奨になった機能
* `Field.from_db_value()`と`Expression.convert_value()`の`context`引数
	* `context`引数は常に空の辞書として使われる
	* Django3.0で削除予定
* その他
	* https://docs.djangoproject.com/en/2.0/releases/2.0/#id1

## まとめ

Djnago2.0の変更点について見てきましたが、移行準備として1.11の環境のうちから`RemovedInDjango20Warning`を解消しておきましょう。（`RemovedInDjango20Warning`は`$ python -Wd manage.py`で検出できます。）