您的位置:首页 > 产品设计 > UI/UE

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

2017-02-05 12:16 736 查看
这篇文章的主要目的是分享学习<<深入理解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_required
def 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_loader
def load_user(userid):
from .models import User
return User.objects(id=userid)

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

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

问题的根本原因使用mongodb后,所有使用相关数据库操作的代码都要去适配。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息