web-dev-qa-db-ja.com

Djangoのビューの複数のデコレータ:実行順序

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デコレータと異なるのはなぜですか?

32
ustun

これで、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
_
32
second

デコレータは、ソースに表示される順序で適用されます。したがって、2番目の例:

@login_required
@active_required
def foo(request):
    ...

以下と同等です。

def foo(request):
    ...
foo = login_required(active_required(foo))

したがって、あるデコレータのコードが別のデコレータによって設定された(または保証された)ものに依存している場合は、依存するデコレータを依存するデコレータの「内部」に配置する必要があります。

ただし、Chris Prattが指摘しているように、デコレータの依存関係は避ける必要があります。必要に応じて、両方を正しい順序で呼び出す単一の新しいデコレータを作成します。

15
dcrosta

デコレータが本当にユニークな機能を持っている場合にのみ、デコレータをスタックすることは本当に意味があります。あなたの説明に基づいて、あなたがactive_requiredを使いたいと思うシナリオは決してありませんが、notlogin_required。したがって、両方をチェックしてそれに応じて分岐するlogin_and_active_requiredデコレータを使用する方が理にかなっています。入力する必要が少なく、文書化する必要が少なく、問題を否定します。

12
Chris Pratt

もう少し説明すると(最初は混乱していました):active_requiredを取り、それをコードでラップするという意味で、my_viewが最初に適用されます。次に、login_requiredが適用され、結果がさらにいくつかのコードにラップされます。

ただし、このラップされたバージョンのmy_viewが実際に呼び出されると、最初にlogin_requiredによって追加されたコードが実行され(ログインしていることを確認)、次にactive_requiredによって追加されたコードが実行されます(アクティブであることを確認します)そして最後にmy_viewが実行されます。

5