Djangoのfilterを自作してみる

Djangoには便利なフィルターが多数用意されていますが、自分の使いたい機能が足りないと思うこともあります。
**無いなら作ればいいじゃないか!**精神のもとDjangoのフィルターを自作してみます。

そもそもフィルターって?

Djangoにはフィルターと呼ばれるtemplateの変数をtemplate内で成形してくれる便利な関数があります。
以下はその一例です。

<p>{{ value | upper }}<p>

この例ではvalueの文字をすべて大文字にして出力するフィルターです。

じゃあ自作しよう

どこに記述するの?

myprojectというプロジェクトとその中にmyappというアプリケーションがある設定で行きます。

./myproject/myapp
├── admin.py
├── apps.py
├── models.py
├── templatetags
│   └── myfilters.py
├── tests.py
└── views.py

上記のようmyappディレクトリの中ににtemplatetagsというディレクトリを作ります。
その中に専用の.pyファイルを作り、そこに記述します。
上の例ではmyfilters.pyになります。

どうやって書くの?

冒頭にも述べたようにフィルターはtemplate内で利用できる関数なので、普通の関数と同じように定義して、そこにちょっと手を加えるだけです。

def onlyalnum(value):
  result = ""
  for c in value:
    if c.isalnum():
      result += c
  return result

myfilter.pyにまず普通に関数を定義しました。
引数で受け取った文字列から数字とアルファベット以外をはぎ取る処理をしています。

これに以下のようにデコレータをつけることでtemplate内で利用することができる形になります。

from django import template # 追記 必須
register = template.Library() # 追記 必須

@register.filter(name="onlyalnum") # デコレータをつける
def onlyalnum(value):
  result = ""
  for c in value:
    if c.isalnum():
      result += c
  return result

register.filterの引数nameを省略すると関数名がそのままフィルターの名前になります。

上の例では関数と同じ名前を付けているので省略しても問題ないのですが、わかり易くするためにつけておくことを推奨します。

引数をとるフィルター

from django import template
register = template.Library()

@register.filter(name="myfilter") # デコレータをつける
def myfilter(value,arg):
   ...

引数を受け取るフィルターは関数を定義する際、引数を受け取るようにしてあげればOKです。

第一引数がフィルター処理される値、第二引数がフィルターに渡す引数になります。

template内で利用するときは以下のように引数を渡してあげます。

{{ value|myfilter:"arg" }}

フィルター名の後ろにコロン(:)をつけて、続けて引数を渡します。

使ってみる

実はフィルターは定義しただけではtemplate内で使えません

template内で適切に読み込んであげる必要があります。

具体的には各templateファイルの先頭に以下のように書き足します。

{% load myfilter %}

loadタグを利用してmyfilter.pyを利用することを宣言します。

これで晴れて自作のフィルターが利用できるようになりました。

{{ value | onlyalnum }}

{{ value | myfilter:"arg"}}

最後に

自分でフィルターが作れるようになったことだし、じゃんじゃん使っていきましょう!