LoginSignup
8
5

More than 5 years have passed since last update.

DjangoにおけるDecoratorの使い方とその作り方

Posted at

背景

Djangoでwebappを作ってるんだけど、Adminページのdocumentを読んでみると、以下の2つのコードは置き換えが可能らしい。

admin.py
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ありの場合と無しの場合を比較してみる。

example1.py

#Decorator あり
  @login_required
  def my_view(request):
   return HttpResponse()


#Decoratorなし
  def my_view(request):
   return HttpResponse

  my_view = login_required(my_view)

example2.py
#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の役割そのものでしか無い。

example3.py
def identity(a_view):
  return a_view

この場合は、上のdecoratorはmy_view(request)と同じ仕事をしてくれるんだとか。うーん、まだいまいち分からない。。

ステップ2

実際になんかしら仕事をするdecoratorを作ってみることにする。試しにviewの呼ばれた回数のログを取っていくdecoratorを作ってみる。

example4.py
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に追加する。

example5.py
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

やっぱりよく分からないんだけど、物覚えと理解力がへぼいのかな。。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5