Hai Tran
Hai Tran

Reputation: 107

Request Handler with @cached_property in GAE with python

Sorry for nerd question, but I spent 2 days but cannot find any solution.

This is my route.py configuration

app = WSGIApplication(
    routes=[
        Route("/account", handler="app.account.Settings")
    ]
)

I created a Handler like that (which required login)

@HealthPortRequestHandler.login_required
class Settings(HealthPortRequestHandler):
    def get(self):
        self.render("account/settings.html")

And create login_required validation method

@cached_property
    def check_user_logged_in(self):
        if self.request.cookies.get("User"):
            user_id = self.read_cookie("User")
            if user_id:
                from models.users import Users
                return Users.get_by_id(int(user_id))
            else:
                return None
        return None

    @staticmethod
    def login_required(handler):
        def check_login(self, *args, **kwargs):
            if self.check_user_logged_in:
                return handler(self, *args, **kwargs)
            else:
                return self.redirect("/login")
        return check_login

But error found when debugging at this line

if self.check_user_logged_in:

with error raise AttributeError(attr)

AttributeError: check_user_logged_in

How can i know exactly happened ? any ideas ?

Upvotes: 0

Views: 127

Answers (1)

snakecharmerb
snakecharmerb

Reputation: 55724

You are applying the decorator login_required to the class Settings, so the decorator will be executed when a Settings instance is created.

When you try to create an instance of Settings, the check_login function inside login_required is called. The arguments it receives are the values that are passed to Setting's __init__ method. check_login then takes the first value that it has received as an argument and tries to access its check_user_logged_in attribute. This value, whatever it is (probably a webapp2.Request object), doesn't have a check_user_logged_in attribute so the exception is raised.

This can be demonstrated without using the webapp2 framework:

class HealthPortRequestHandler(object):

    def __init__(self, *args, **kwargs):
        pass

    @property
    def check_user_logged_in(self):
        pass

    @staticmethod
    def login_required(handler):
        def check_login(self, *args, **kwargs):
            if self.check_user_logged_in:
                return handler(self, *args, **kwargs)
            else:
                raise RuntimeError('Not expecting to execute this')
        return check_login   


@HealthPortRequestHandler.login_required
class Settings(HealthPortRequestHandler):
    pass

>>> s = Settings('Hello')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "demo.py", line 14, in check_login
    if self.check_user_logged_in:
AttributeError: 'str' object has no attribute 'check_user_logged_in'

>>> s = Settings(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "demo.py", line 14, in check_login
    if self.check_user_logged_in:
AttributeError: 'int' object has no attribute 'check_user_logged_in'

>>> s = Settings('Hello', 5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "deco.py", line 14, in check_login
    if self.check_user_logged_in:
AttributeError: 'str' object has no attribute 'check_user_logged_in'

So that's why you get the AttributeError.

To fix it, I'd suggest decorating the request handler's dispatch method instead of the class definition. This way you can still make your checks before the request is handled, but with access to the fully initialised Settings instance.

Upvotes: 1

Related Questions