2012-01-03 15 views
28

2つのデコレータ(ログインの確認用とis_activeの確認用)でDjangoビューをデコレートしようとしています。Djangoのビュー用の複数のデコレータ:実行順序

最初のものは、内蔵で@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デコレータと異なる理由は何ですか?

答えて

29

、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

ええ、私はまだ注文について少し混乱しています:http://stackoverflow.com/a/739665/72436とhttp://stackoverflow.com/a/8715839/72436そうでないと示唆しています。 – ustun

+1

OK、私はあなたがそれを釘付けにしたと思います。違いは、それを呼び出す関数と関数を返すことにあります。 – ustun

10

本当にユニークな機能を持つデコレータをスタックするのは本当に理にかなっています。あなたの説明に基づいて、active_requiredではなくlogin_requiredではないシナリオになることはありません。したがって、login_and_active_requiredデコレータを使用して、両方をチェックし、それに応じてブランチを作成する方が理にかなっています。入力しにくく、文書化するのが少なく、問題を否定します。今

+0

OK、Djangoの組み込みコードはカスタムコードよりも信頼性が高いと思われます。 Djangoがこのデコレータを持っていないのは少し奇妙ですが、それはかなり一般的なはずです。私はWONTFIXとマークされたいくつかのバグレポートを見ました。 – ustun

+0

合意。 'is_active'が組み込まれていて、ほとんどのシナリオで' login_required'をほとんど否定するので、開発者はこのアウトオブボックスを占めていたはずです。 –

13

デコレータは、彼らがソースに現れる順に適用されます。このように、あなたの第二の例:

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

したがって、1つのデコレータのコードが何か(または保証)によって設定された他に依存している場合、あなたが入れてあります。

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

は以下と等価です従属デコレータは、陥没型デコレータの内部にある。

しかし、Chris Pratt氏によれば、デコレータの依存関係を避けるべきです。必要に応じて、両方を正しい順序で呼び出す新しいデコレータを1つ作成します。

+0

OKですが、逆にする必要があります。まず、login_requiredを適用してからactive_requiredを適用します。 私は与えた最初の例と同じではないでしょうか? – ustun

+0

この場合、 'login_required'が最初に適用されます。' foo'の呼び出しは、 'login_required'によって返された関数と、' active_required'によって返された関数を最初に通過するという考え方です。あなたはPDBでそれを実行したり、デバッギング 'print'sを追加して私が何を意味するのかを知ることができます。 – dcrosta

4

これをもう少し説明すると(私も最初は混乱していた):active_requiredは、my_viewが最初に適用され、コードでラップされています。次にlogin_requiredが適用され、結果がさらにコードにラップされます。 my_viewのこの包まれたバージョンが実際に呼び出されたときに

しかし、最初のlogin_requiredによって追加されたコードが実行され、その後、active_requiredによって追加されたコードが実行される(あなたがログインしていることを確認する)(あなたがアクティブだと確認する)と最後にmy_viewが実行されます。

+0

それはここのコメントの混乱を明らかにするために差別化するための良い方法です:http://stackoverflow.com/a/8715821/781695 – Medorator