这篇文章的主要目的是分享学习<<深入理解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后,所有使用相关数据库操作的代码都要去适配。
新闻热点
疑难解答