9

ユーザーが Web アプリケーション (Google App Engine で実行されている) 内のリソースにアクセスする方法とアクセスしない方法を管理するために、デコレータを使用しようとしています。ユーザーが Google アカウントでログインすることを許可していないため、app.yaml 内の特定のルートに特定のアクセス権を設定することはできません。

私は次のリソースを使用しました:
- Bruce Eckel のデコレータのガイド
- SO : get - class
- in-python - decorator2

しかし、私はまだ少し混乱しています...

これが私のコードです!次の例では、 current_user は RequestHandler クラスに属する @property メソッドです。データストアに格納された User(db.model) オブジェクトをレベル IntProperty() で返します。

class FoobarController(RequestHandler):

    # Access decorator
    def requiredLevel(required_level):
        def wrap(func):
            def f(self, *args):
                if self.current_user.level >= required_level:
                    func(self, *args)
                else:
                    raise Exception('Insufficient level to access this resource') 
            return f
        return wrap

    @requiredLevel(100)
    def get(self, someparameters):
        #do stuff here...

    @requiredLevel(200)
    def post(self):
        #do something else here...

ただし、私のアプリケーションでは、さまざまな種類のリソースに対してさまざまなコントローラーを使用しています。すべてのサブクラス内で @requiredLevel デコレーターを使用するには、それを親クラス (RequestHandler) に移動する必要があります。

class RequestHandler(webapp.RequestHandler):

    #Access decorator
    def requiredLevel(required_level):
        #See code above

私の考えは、次のコードを使用して、すべてのコントローラー サブクラスのデコレーターにアクセスすることです。

class FoobarController(RequestHandler):

    @RequestHandler.requiredLevel(100)
    def get(self):
        #do stuff here...

デコレータとクラス継承に関する私の知識の限界に達したと思います:)。何かご意見は ?

4

2 に答える 2

4

2 つの小さな調整を加えた元のコードも機能するはずです。クラスベースのアプローチは、このような単純なデコレーターにとってかなり重いようです。

class RequestHandler(webapp.RequestHandler):

    # The decorator is now a class method.
    @classmethod     # Note the 'klass' argument, similar to 'self' on an instance method
    def requiredLevel(klass, required_level):
        def wrap(func):
            def f(self, *args):
                if self.current_user.level >= required_level:
                    func(self, *args)
                else:
                    raise Exception('Insufficient level to access this resource') 
            return f
        return wrap


class FoobarController(RequestHandler):
    @RequestHandler.requiredLevel(100)
    def get(self, someparameters):
        #do stuff here...

    @RequestHandler.requiredLevel(200)
    def post(self):
        #do something else here...

@staticmethodまたは、代わりにa を使用できます。

class RequestHandler(webapp.RequestHandler):

    # The decorator is now a static method.
    @staticmethod     # No default argument required...
    def requiredLevel(required_level):

元のコードが機能しなかった理由は、requiredLevel がインスタンス メソッドであると想定されていたためです。これは、クラス宣言時 (他のメソッドを装飾していたとき) には使用できず、クラス オブジェクト (デコレータを RequestHandler ベース クラスに配置することは優れたアイデアであり、結果として得られるデコレータ呼び出しは自己文書化されています)。

@classmethodおよびに関するドキュメントを読むことに興味があるかもしれません@staticmethod

また、デコレーターに入れるのが好きな定型文を少し:

    @staticmethod
    def requiredLevel(required_level):
        def wrap(func):
            def f(self, *args):
                if self.current_user.level >= required_level:
                    func(self, *args)
                else:
                    raise Exception('Insufficient level to access this resource') 
            # This will maintain the function name and documentation of the wrapped function.
            # Very helpful when debugging or checking the docs from the python shell:
            wrap.__doc__ = f.__doc__
            wrap.__name__ = f.__name__
            return f
        return wrap
于 2010-08-27T15:46:47.627 に答える
1

StackOverflowを掘り下げて、 Bruce Eckelのデコレータガイドを注意深く読んだ後、考えられる解決策を見つけたと思います。

これには、デコレータを親クラスのクラスとして実装することが含まれます。

class RequestHandler(webapp.RequestHandler):

    # Decorator class :
    class requiredLevel(object):
        def __init__(self, required_level):
            self.required_level = required_level

        def __call__(self, f):
            def wrapped_f(*f_args):
                if f_args[0].current_user.level >= self.required_level:
                    return f(*f_args)
                else:
                    raise Exception('User has insufficient level to access this resource') 
            return wrapped_f

これでうまくいきます!f_args [0]を使用するのは少し汚いようですが、もっときれいなものが見つかったら、この回答を編集します。

次に、次の方法でサブクラスのメソッドを装飾できます。

FooController(RequestHandler):
    @RequestHandler.requiredLevel(100)
    def get(self, id):
        # Do something here

    @RequestHandler.requiredLevel(250)
    def post(self)
        # Do some stuff here

BarController(RequestHandler):
    @RequestHandler.requiredLevel(500)
    def get(self, id):
        # Do something here

コメントするか、拡張機能を提案してください。

于 2010-07-31T18:31:09.563 に答える