#背景
Djangoでwebappを作ってるんだけど、Adminページのdocumentを読んでみると、以下の2つのコードは置き換えが可能らしい。
from django.contrib import admin
from .models import Author
#decorator無し
class AuthorAdmin(admin.ModelAdmin):
pass
admin.site.register(Author, AuthorAdmin)
#decorator有り
@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
pass
まぁこれが一個だけだと何もありがたみを感じないんだけど、admin.site.register(sth, sthAdmin)ってのが何十個も並び出すと、目に優しく無いからこれで代用する方がすっきりするんだとか。
で、decoratorってなんやねん!って思ったので、調べて何となくわかった範囲でまとめてみる。
元記事はこちら。もうちょっと経験値が上がってからじゃないと納得して理解して使えない系の話題みたい。少なくとも俺にとっては。
わかったことだけ先にまとめてしまうと、
- functionとかclassの上で定義してあげると、なんらかの効果を付与してくれる。
- 例えば、viewの実行をログインしてるユーザーだけに限定したり
- ログをとったりclassを定義すると勝手にそれを実行したり
まぁ便利みたいだから、登場した度に一個ずつ覚えて行こうと思う。ではスタート。
#Decoratorsは関数
面倒臭い@の概念に関してはとりあえず無視して、Decoratorありの場合と無しの場合を比較してみる。
#Decorator あり
@login_required
def my_view(request):
return HttpResponse()
#Decoratorなし
def my_view(request):
return HttpResponse
my_view = login_required(my_view)
#Decoratorあり
@require_http_method(['GET','POST'])
def my_view(request):
return HttpResponse()
#Decoratorなし
def my_view(request):
return HttpResponse()
my_view = require_http_method(['GET'],['POST'])(my_view)
#Decoratorのパラメータのない場合
これまでの説明からもわかるように、decoratorはただの関数に等しいもので、後に続く関数をパラメーターとして受け取り、役割を引き継ぐみたい。(これで解釈して現状問題なし)
例えばmy_view(request)が呼ばれた時には、実際にはlogin_required(my_view)(request)が呼ばれていることに等しいらしい
ここで一個注意しておきたいのは、Decoratorはcodingするときのdesign patternの一種であってpython や Djangoに特有のものでは無いんだとか
だからpythonの知識がある程度あれば、decoratorは自分で作れちゃうらしい。ということで、decoratorの作り方を少しずつ見ていくことにする。
##はじめに
identity decoratorを作る。何をするわけでもなく、ただ関数一個受け取ってそれを返すだけ。djangoでいうviewの役割そのものでしか無い。
def identity(a_view):
return a_view
この場合は、上のdecoratorはmy_view(request)と同じ仕事をしてくれるんだとか。うーん、まだいまいち分からない。。
##ステップ2
実際になんかしら仕事をするdecoratorを作ってみることにする。試しにviewの呼ばれた回数のログを取っていくdecoratorを作ってみる。
def log(a_view):
def _wrapped_view(request, *args, **kwargs):
logger.log('My view is called')
return a_view(request, *args, **kwargs)
return _wrapped_view
ここで作ったものは
- なんかしらのlogをとって
- parametarで受け取ったviewをそのまま返す
なんともシンプルなdecorator。実行される手順としては以下の手順。
- log(my_view)(request)
- _wrapped_view(request)
- my_view(request)
なんか実際に意味があるのかわからないけど、とりあえずこれでログが取れるらしい。
試しに次はwrapped_viewを実行しようとしているユーザーがログインしてるかどうかをみる機能をこのdecoratorに追加する。
def login_required(a_view):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return a_view(request, *args, **kwargs)
return HttpResponseForbiden()
return _wrapped_view
やっぱりよく分からないんだけど、物覚えと理解力がへぼいのかな。。