6

私は Python Flask アプリを作成しており、以下のデコレータとビューを作成しています。デコレーターは、インデックスを表示するときにうまく機能しますが、ログアウトしてurl_forインデックスを使用してリダイレクトすると、builderror がスローされます。なぜだろう

def logged_in(fn):
    def decorator():
        if 'email' in session:
            return fn()
        else:
            return render_template('not-allowed.html', page="index")
    return decorator


@app.route('/')
@logged_in
def index():
    email = session['email']    
    return render_template('index.html', auth=True, page="index", marks=marks)

@app.route('/sign-out')
def sign_out():
    session.pop('email')
    print(url_for('index'))
    return redirect(url_for('index'))

何か案は?エラーは次のとおりです。BuildError: ('index', {}, None)

4

1 に答える 1

13

ここでの問題は、decorator()返された関数が装飾している関数とは異なる名前を持っているため、URL ビルダーがindexビューを見つけられないことです。元の関数の名前をコピーするには、モジュールからwraps()デコレータを使用する必要があります。functools別の問題 (まだ遭遇する必要があります) は、デコレータで引数を受け入れず、それを元の関数に渡さないことです。修正されたデコレータは次のとおりです。

from functools import wraps

def logged_in(fn):
    @wraps(fn)
    def decorator(*args, **kwargs):
        if 'email' in session:
            return fn(*args, **kwargs)
        else:
            # IMO it's nicer to abort here and handle it in errorhandler.
            abort(401)
    return decorator

もう少し説明: Python のデコレータは、引数として別の関数を取り、その結果として関数を返す関数です。したがって、次の

@logged_in
def index(): pass

と本質的に同じです

def index(): pass
index = logged_in(index)

この場合の問題は、logged_inデコレーターが返すのは元の関数ではなく、元の関数decoratorをラップするラッパー (コードで呼び出される) であるということです。decoratorこのラッパーは、ラップしている元の関数とは異なる名前 ( ) を持っています。app.route()の後に呼び出すデコレータは、logged_inこの新しい関数を認識し、その名前 ( decorator) を使用してルートを登録します。ここに問題があります: 装飾された関数に同じ名前 ( ) を持たせたいので、その関数のルートを取得するためにindex使用できます。url_for()そのため、名前を手動でコピーする必要があります

decorator.__name__ = fn.__name__

または、モジュールのヘルパーをより適切に使用update_wrapperしてください。これは、それ以上のことを行います。wrapsfunctools

于 2013-01-01T21:57:08.073 に答える