Djangoがちょっとだけ便利になるかも知れないライブラリを作った話

平成最後の4月(2019/4/27)にDjangoがちょっとだけ便利になるかも知れないライブラリを公開して、先日1万DLを突破したので記事を書きたいと思います。  

## Django-Boost  

> GitHub
https://github.com/ChanTsune/django-boost

### そもそもなんで作ったの？
INIADでは講義内でPython製のWebアプリケーションフレームワークDjangoを学習する機会があるので、その時使ったDjangoを便利に使えるようにしようと思ったのがきっかけです。

### 何ができるの？

Djangoで開発しているときにやりたくなることがちょっとだけお手軽にできます。
具体的には、

- メールアドレスでログインするユーザーを簡単に使える
- Djangoテンプテートのなかで、Pythonの組み込み関数が使える
- URLの定義を見やすく書ける
- Http404以外の例外を投げることができる
- 一定時間ごとに再認証を要求するページが作れる
- Formクラスの中でログインしているユーザーの情報に触れる
- herokuにアプリケーションをデプロイするのがちょっとだけ簡単になる

他にも細々とした機能がたくさんありますが、細かくてかつ地味なので今回は割愛します。
詳しくは、[ドキュメント](https://django-boost.readthedocs.io/)をご覧ください。  

## 導入  
```bash
$ pip install django_boost
```
pipで入ります。

### 設定
Djangoのプロジェクトで`django_boost`が有効になるように設定を加えます。  

```py:settings.py


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_boost', # 追記
]

```

## 機能毎の紹介

### メールアドレスでログインするユーザー  

設定ファイルに以下の内容を追記します。

```py:settings.py
AUTH_USER_MODEL = 'django_boost.EmailUser'
```
Django標準のユーザーモデルをユーザー名ではなくメールアドレスでログインするユーザーに置き換えます。
Django標準のユーザーモデルと持っているフィールドは同じです。  


### Djangoテンプテートのなかで、Pythonの組み込み関数を使う

以下の記述をテンプレートファイルに加えることで、Djangoテンプレートの中でPythonの組み込みの関数が、フィルターやタグとして使えるようになります。

```django:template.html
{% load boost %}
```

> Pythonの組み込み関数一覧
> https://docs.python.org/ja/3/library/functions.html

個人的に、文字列の整形の際に`format`関数をよく使っています。  

```django
{% load boost %}

{{ value|format:"," }} {# 数字を3桁毎にカンマ区切りで表示 #}
```

`zip`とかも割と便利です。  
あとは開発中だと、テンプレートの中で変数の型や属性の一覧が見れる、`type`や`dir`あたりも便利だと思います。  

組み込みの関数ではありませんが、文字列リテラルからオブジェクトが作れるタグもあります。  

以下の例だと、リストを作成しています。  

```django:template.html
{%load boost %}

{% literal "[1,2,3]" as lst %}

{% for i in lst %}
  <p>{{ i }}</p>
{% endfor %}
```

### URLの定義を見やすく書く
一つのアプリケーションに複数のモデルを作っていると段々と`urlpatterns`が見辛くなってくると思います。　
かといってファイルを分割するのも面倒な時に活用できます。  

```py:urls.py
from django_boost.urls import UrlSet

class YourModelUrlSet(UrlSet):
    app_name = "YourModel"
    urlpatterns = [
        path('xxx/', ..., name="XXX")
        path('yyy/', ..., name="YYY")
        path('zzz/', ..., name="ZZZ")
    ]

urlpatterns = [
    path('path/to/model/', include(YourModelUrlSet))
]
```
それぞれのモデルのCRUDviewごとに分けると見やすさが上がります。
モデルごとに名前空間の切り分けもできるのも地味に便利だと思います。


### Http404以外の例外を投げる

閲覧できる権限の条件を満たさなかった時に404をだすのはよくやると思います。
それ以外の例外も投げられるようにできます。  

利用するには設定ファイルにちょっとだけ追記が必要です。  

```py:settings.py
MIDDLEWARE = [
    'django_boost.middleware.HttpStatusCodeExceptionMiddleware',  # 追記
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
```
ミドルウェアを一個足しました。  

この状態で、viewの処理の中で例外を投げるとそのステータスコードが返るようになります。
以下の例では`Http302`例外を投げてリダイレクトさせています。  

```views.py
from django.shortcuts import render
from django_boost.http.response import Http302

def my_view(request):
  if request.user.email == "":
     raise Http302("/register/email/")
  return render(request, "mypage.html")

```
302以外にも、大体のステータスコードを網羅しています。  

カスタムテンプレートを利用したい場合は、templatesディレクトリの直下に`[ステータスコード].html`を配置するとそれを利用するようになります。  

### 一定時間ごとに再認証を要求するページ

クラスベースでViewを作っている場合、以下のようにミックスインクラスを継承させることで、一定期間ごとに認証を要求するページを作れます。  

```py:view.py
from datetime import timedelta
from django_boost.views.mixins import ReAuthenticationRequiredMixin

class MyView(ReAuthenticationRequiredMixin,TemplateView):
    template_name = '...'
    auth_unnecessary = timedelta(hours=1)

```
クラス変数`auth_unnecessary`再認証の間隔を設定することで最終ログイン時刻から設定した間隔以上の期間が空いた時、再認証を要求するようになります。  

`auth_unnecessary`には`int`で秒数を指定するほか、`timedelta`を利用して指定することができます。  

### Formクラスの中でログインしているユーザーの情報に触る

フォームのバリデーションを行う際に、ユーザー毎にバリデーションの条件を変えたいなんてことはよくあると思います。  

そんな時には、FormクラスとViewクラスにそれぞれ以下のミックスインクラスを継承することでformクラス内から`self.user`でユーザーの情報に触れるようになります。  

```py:forms.py
from django import forms
from django_boost.forms.mixins import FormUserKwargsMixin

class MyForm(FormUserKwargsMixin,Form):
   email = forms.EmailField()

   def email_clean(self):
      if self.user.email: # ログインしているユーザーのメールアドレス
          ...
      ...
```

```py:views.py
form django_boost.views.mixins import ViewUserKwargsMixin
from .forms import MyForm

class MyView(ViewUserKwargsMixin,FormView):
   form_class = MyForm
   ...
```

### herokuにアプリケーションをデプロイするのをちょっとだけ簡単に

[heroku](https://jp.heroku.com/)にDjangoアプリケーションをデプロイする際に必要な設定ファイルを自動生成するコマンドが使えます。

```bash
$ python manage.py support_heroku
```
```bash
Generated : /Users/username/project/Procfile
Generated : /Users/username/project/runtime.txt
Generated : /Users/username/project/requirements.txt
```

`Procfile`,`runtime.txt`,`requirements.txt`が自動で生成されます。

同名のファイルが存在する時は、生成されないので上書きして生成したいときは、`--overwrite`オプションを加えます。  

```bash
$ python manage.py support_heroku --overwrite
```


### 最後に  
冒頭にも述べましたが、この他にも細々とした地味な機能がたくさんありますので、気になるになる方は、[ドキュメント](https://django-boost.readthedocs.io/)をご覧ください。


星がもらえると、開発者は喜びます！
https://github.com/ChanTsune/django-boost