新手学习Flask引用flask-login后登入错误原因不清

自己在学习Flask时,使用flask-login遇到问题。

程序是一个很简单的登录鉴权的程序,如果用户输入用户名和密码正确,则可以查看到首页,否则回到登录页面让用户输入用户名和密码。

目前遇到的问题是,输入错误的用户名和密码不可登录(正常),但输入正确的用户名和密码后,程序报错。

开启Flask调试模式,报错信息如下:

builtins.TypeError TypeError: 'NoneType' object is not callable

Traceback (most recent call last) File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1836, in call return self.wsgi_app(environ, start_response)
File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1820, in wsgi_app response =
self.make_response(self.handle_exception(e)) File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1403, in handle_exception reraise(exc_type, exc_value, tb) File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/_compat.py",
line 33, in reraise raise value File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1817, in wsgi_app response = self.full_dispatch_request() File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1477, in full_dispatch_request rv = self.handle_user_exception(e)
File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1381, in handle_user_exception reraise(exc_type, exc_value, tb)
File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/_compat.py",
line 33, in reraise raise value File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1475, in full_dispatch_request rv = self.dispatch_request() File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask/app.py",
line 1461, in dispatch_request return
self.view_functionsrule.endpoint File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask_login.py",
line 756, in decorated_view elif not current_user.is_authenticated():
File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/werkzeug/local.py",
line 338, in getattr return getattr(self._get_current_object(),
name) File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/werkzeug/local.py",
line 297, in _get_current_object return self.__local() File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask_login.py",
line 46, in current_user = LocalProxy(lambda: _get_user())
File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask_login.py",
line 794, in _get_user current_app.login_manager._load_user() File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask_login.py",
line 363, in _load_user return self.reload_user() File
"/Users/duzhipeng/project/xianpi/venv/lib/python3.4/site-packages/flask_login.py",
line 325, in reload_user user = self.user_callback(user_id) TypeError:
'NoneType' object is not callable The debugger caught an exception in
your WSGI application. You can now look at the traceback which led to
the error. To switch between the interactive traceback and the
plaintext one, you can click on the "Traceback" headline. From the
text traceback you can also create a paste of it. For code execution
mouse-over the frame you want to debug and click on the console icon
on the right side.

You can execute arbitrary Python code in the stack frames and there
are some extra helpers available for introspection:

dump() shows all variables in the frame dump(obj) dumps all that's
known about the object

主程序就两个文件,源码分别如下:
run.py

# -*- coding: utf-8 -*-
import os
from flask import Flask, render_template, redirect, url_for, flash

from flask.ext.sqlalchemy import SQLAlchemy

from flask_wtf import Form
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired

from flask.ext.login import LoginManager, login_required, login_user


basedir = os.path.abspath(os.path.dirname(__file__))


app = Flask(__name__)
app.config['SECRET_KEY'] = 'stong key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + os.path.join(basedir, 'date.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.session_protection = 'strong'
login_manager.login_view = 'login'


class LoginForm(Form):
    username = StringField('用户名', validators=[DataRequired()])
    password = PasswordField('密码', validators=[DataRequired()])
    submit = SubmitField()


@app.route('/')
@login_required
def index():
    return 'hello'


@app.route('/login', methods=['GET', 'POST'])
def login():
    from models import Admin
    form = LoginForm()
    if form.validate_on_submit():
        user = Admin.query.filter_by(username=form.username.data).first()
        if user is not None and user.verify_password(form.password.data):
            login_user(user)
            return redirect(url_for('index'))
        flash('用户名或密码错误')
    return render_template('login.html', form=form)


if __name__ == '__main__':
    app.run(debug=True)

models.py

# -*- coding: utf-8 -*-

from werkzeug.security import generate_password_hash, check_password_hash
from flask.ext.login import UserMixin
from run import db, login_manager

class Admin(UserMixin, db.Model):
    __tablename__ = 'admin'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True)
    password_hash = db.Column(db.String(128), unique=False)

    @property
    def password(self):
        raise AttributeError('不能直接获取明文密码!')

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return "<Admin '{:s}>".format(self.username)



@login_manager.user_loader
def load_user(user_id):
    return Admin.query.get(int(user_id))

if __name__ == '__main__':
    db.create_all()

我自己觉得是login_manager.user_loader 回调有问题,user_id从哪里来的也弄不明白。

自己GG和看了例子大概3个小时了,还是无解,因此特来请大神解惑。

谢谢!

阅读 13.1k
评论 2015-06-06 提问
    1 个回答
    6d6f33
    • 169

    自己知道错误在哪里了。

    @login_manager.user_loader
    def load_user(user_id):
        return Admin.query.get(int(user_id))
    

    @login_manager.user_loader 写到models里了,导致没有被调用生效。改到run里就可以了。不过这和书上的不同,例子也不同。这点我还得研究研究。

    评论 赞赏 2015-06-06
      撰写回答

      登录后参与交流、获取后续更新提醒