Djangoビューを2つのデコレータで装飾しようとしています。1つはログインをチェックするためのもので、もう1つはis_activeをチェックするためのものです。
1つ目は組み込みの@login_required
で、2つ目は次のとおりです。
def active_required(function):
dec = user_passes_test(lambda u: u.is_active, '/notallowed', '')
return dec(function)
これで、Pythonのデコレータは裏返しに機能しますが、以下は機能しません。
@active_required
@login_required
def foo(request):
...
最初にユーザーがログインしているかどうかを確認し、ログインしていない場合はログインページにリダイレクトし、ログインしている場合はアクティブかどうかを確認し、ログインしていない場合は'/notallowed'
にリダイレクトします。
Login_requiredが失敗した場合、ユーザーはログインページにリダイレクトされませんが、@active_required
が実行されます。その場合、ユーザーはnullであるため、@ active_requiredデコレータは失敗し、ユーザーは/notallowed
にリダイレクトされます。
順序を変更するとうまくいくようですが、
@login_required
@active_required
def foo(request):
...
しかし、私はこのアプローチにも何か問題があるのではないかと思います。
2つのデコレータを組み合わせる適切な方法は何ですか?また、実行順序が単純なPythonデコレータと異なるのはなぜですか?
これで、Pythonのデコレータは裏返しに機能します
まあ、それは裏返しのあなたの定義に依存すると思います。あなたの場合、_login_required
_を最初に実行したいので、それは「最も外側の」(一番上の)デコレータである必要があります
あなたが指摘したように、あなたの最後の例はうまくいきます、そして確かにこれをする正しい方法です
編集
多分混乱は(これらの特定の)デコレータがどのように機能するかです
login_required(original_view)
は新しいビューを返します。このビューは、最初にログインしているかどうかを確認してから、original_viewを呼び出します。
そう
_login_required(
active_required(
my_view
)
)
first checks if you are logged in, then
first(second) checks if you are active, then
runs my_vew
_
デコレータは、ソースに表示される順序で適用されます。したがって、2番目の例:
@login_required
@active_required
def foo(request):
...
以下と同等です。
def foo(request):
...
foo = login_required(active_required(foo))
したがって、あるデコレータのコードが別のデコレータによって設定された(または保証された)ものに依存している場合は、依存するデコレータを依存するデコレータの「内部」に配置する必要があります。
ただし、Chris Prattが指摘しているように、デコレータの依存関係は避ける必要があります。必要に応じて、両方を正しい順序で呼び出す単一の新しいデコレータを作成します。
デコレータが本当にユニークな機能を持っている場合にのみ、デコレータをスタックすることは本当に意味があります。あなたの説明に基づいて、あなたがactive_required
を使いたいと思うシナリオは決してありませんが、notlogin_required
。したがって、両方をチェックしてそれに応じて分岐するlogin_and_active_required
デコレータを使用する方が理にかなっています。入力する必要が少なく、文書化する必要が少なく、問題を否定します。
もう少し説明すると(最初は混乱していました):active_required
を取り、それをコードでラップするという意味で、my_view
が最初に適用されます。次に、login_required
が適用され、結果がさらにいくつかのコードにラップされます。
ただし、このラップされたバージョンのmy_view
が実際に呼び出されると、最初にlogin_required
によって追加されたコードが実行され(ログインしていることを確認)、次にactive_required
によって追加されたコードが実行されます(アクティブであることを確認します)そして最後にmy_view
が実行されます。