<<深入理解Flask>>中遇到的那些坑

来源:互联网 发布:淘宝一飞老师 编辑:程序博客网 时间:2024/05/21 11:12

这篇文章的主要目的是分享学习<<深入理解Flask>>这本书中遇到的坑,可以使遇到相同问题的同学少走弯路。大家有遇到或解决过相关问题的,也欢迎分享出来。

坑一:

在把博客从sqlite重构到mongodb时,在登陆成功后想要访问只有登陆用户才能访问的页面时,遇到了下面的错误:

in decorated_view
    elif not current_user.is_authenticated:
  File "/usr/local/lib/python3.4/dist-packages/werkzeug/local.py", line 343, in __getattr__
    return getattr(self._get_current_object(), name)

AttributeError: 'BaseQuerySet' object has no attribute 'is_authenticated'


我是在登陆成功后,准备写新的博客时遇到的这个错误。可以看到,这个函数用login_required装饰器进行了装饰,因此访问这个页面要求有登陆信息:

@blog_blueprint.route('/new', methods=['GET', 'POST'])@login_requireddef new_post():    form = PostForm()    if form.validate_on_submit():       ......    return render_template('new.html', form=form)

进一步查看装饰器的实现:
def login_required(func):      @wraps(func)    def decorated_view(*args, **kwargs):        if request.method in EXEMPT_METHODS:            return func(*args, **kwargs)        elif current_app.login_manager._login_disabled:            return func(*args, **kwargs)        elif not current_user.is_authenticated:            return current_app.login_manager.unauthorized()        return func(*args, **kwargs)    return decorated_view
可以看到出错的语句就是在访问current_user.is_authenticated时报错的.


错误提示说没有'is_authenticated'属性,查看User的models定义:

class User(mongo.Document):   ...    def is_authenticated(self):        if isinstance(self, AnonymousUserMixin):            return False        else:            return True

也是定义了这个属性的,再仔细看出错提示是说 'BaseQuerySet'没有这个属性,为什么会报这个类错误呢,我们的User应该不是这个类啊,因此查看'current_user'的具体实现:

current_user = LocalProxy(lambda: _get_user())

一路跟踪,可以看出最终的user是在下面设置的:

flask_login/login_manager.py:

def reload_user(self, user=None):        ctx = _request_ctx_stack.top        if user is None:            user_id = session.get('user_id')            if user_id is None:                ctx.user = self.anonymous_user()            else:                if self.user_callback is None:                    raise Exception(                        "No user_loader has been installed for this "                        "LoginManager. Add one with the "                        "'LoginManager.user_loader' decorator.")                user = self.user_callback(user_id)...
通过user_id获取user,而这个user_callback就是通过user_id获取user的关键,进一步看这个函数是在何时设置的:

def user_loader(self, callback):        '''        This sets the callback for reloading a user from the session. The        function you set should take a user ID (a ``unicode``) and return a        user object, or ``None`` if the user does not exist.        :param callback: The callback for retrieving a user object.        :type callback: callable        '''        self.user_callback = callback        return callback

前面使用sqlalchemy时曾在extensions.py中设置过这个函数:

@login_manager.user_loaderdef load_user(userid):    from .models import User    return User.objects(id=userid)

可以看到,当时使用的是sqlite,因此这样返回的就是User对象,而现在使用mongodb后,这样返回的只是一个'BaseQuerySet',因此
出现了问题。
修改成下面后,问题解决:

@login_manager.user_loaderdef load_user(userid):    from .models import User    return User.objects(id=userid).first()

问题的根本原因使用mongodb后,所有使用相关数据库操作的代码都要去适配。



0 0