Flask 教程,第五部分:用户登录 已翻译 100%

oschina 投递于 2012/12/28 09:23 (共 17 段, 翻译完成于 01-29)
阅读 78999
收藏 17
3
加载中

这是我用Python的轻量级框架 Flask 编写web程序时的一些经验,我将它记录在此,本篇是一系列文章中的第五篇。

下面是这个系列中已经发布的文章的目录:


r
ranvane
翻译于 2013/01/06 11:15
2

回顾

在前面的系列章节中,我们创建了一个数据库并且学着用用户和邮件来填充,但是到现在我们还没能够植入到我们的程序中。 两章之前,我们已经看到怎么去创建网络表单并且留下了一个实现完全的登陆表单。

在这篇文章中,我们将基于我门所学的网络表单和数据库来构建并实现我们自己的用户登录系统。教程的最后我们小程序会实现新用户注册,登陆和退出的功能。

为了能跟上这章节,你需要前一章节最后部分,我们留下的微博程序。请确保你的程序已经正确安装和运行。

Richard_sun
Richard_sun
翻译于 2013/01/03 18:55
2

在前面的章节,我们开始配置我们将要用到的Flask扩展。为了登录系统,我们将使用两个扩展,Flask-Login 和 Flask-OpenID. 配置如下所示 (fileapp\__init__.py):

import os
from flaskext.login import LoginManager
from flaskext.openid import OpenID
from config import basedir

lm = LoginManager()
lm.setup_app(app)
oid = OpenID(app, os.path.join(basedir, 'tmp'))
Flask-OpenID 扩展为了可以存储临时文件,需要一个临时文件夹路径。为此,我们提供了它的位置。


r
ranvane
翻译于 2013/01/05 22:37
2

重访我们的用户模型

Flask-Login扩展需要在我们的User类里实现一些方法。除了这些方法以外,类没有被要求实现其它方法。

下面是我们的User类 (fileapp/models.py):

class User(db.Model):
    id = db.Column(db.Integer, primary_key = True)
    nickname = db.Column(db.String(64), unique = True)
    email = db.Column(db.String(120), unique = True)
    role = db.Column(db.SmallInteger, default = ROLE_USER)
    posts = db.relationship('Post', backref = 'author', lazy = 'dynamic')

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return unicode(self.id)

    def __repr__(self):
        return '<User %r>' % (self.name)

is_authenticated方法是一个误导性的名字的方法,通常这个方法应该返回True,除非对象代表一个由于某种原因没有被认证的用户。

is_active方法应该为用户返回True除非用户不是激活的,例如,他们已经被禁了。

is_anonymous方法应该为那些不被获准登录的用户返回True。

最后,get_id方法为用户返回唯一的unicode标识符。我们用数据库层生成唯一的id。

se77en
se77en
翻译于 2013/01/24 10:14
2

用户加载回调

现在我们通过使用Flask-Login和Flask-OpenID扩展来实现登录系统

首先,我们需要写一个方法从数据库加载到一个用户。这个方法会被Flask-Login使用(fileapp/views.py):

@lm.user_loader
def load_user(id):
    return User.query.get(int(id))

记住Flask-Login里的user id一直是unicode类型的,所以在我们把id传递给Flask-SQLAlchemy时,有必要把它转化成integer类型。

se77en
se77en
翻译于 2013/01/24 11:00
3

登录视图函数

接下来我们要更新登录视图函数(fileapp/views.py):

from flask import render_template, flash, redirect, session, url_for, request, g
from flaskext.login import login_user, logout_user, current_user, login_required
from app import app, db, lm, oid
from forms import LoginForm
from models import User, ROLE_USER, ROLE_ADMIN

@app.route('/login', methods = ['GET', 'POST'])
@oid.loginhandler
def login():
    if g.user is not None and g.user.is_authenticated():
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        session['remember_me'] = form.remember_me.data
        return oid.try_login(form.openid.data, ask_for = ['nickname', 'email'])
    return render_template('login.html', 
        title = 'Sign In',
        form = form,
        providers = app.config['OPENID_PROVIDERS'])

注意到我们导入了一些新的模块,其中有些后面会用到。

跟上个版本的变化很小。我们给视图函数添加了一个新的装饰器:oid.loginhandler。它告诉Flask-OpenID这是我们的登录视图函数。

李东华
李东华
翻译于 2013/01/12 02:43
5

在方法体的开头,我们检测是是否用户是已经经过登录认证的,如果是就重定向到index页面。这儿的思路是如果一个用户已经登录了,那么我们不会让它做二次登录。

全局变量g是Flask设置的,在一个request生命周期中,用来存储和共享数据的变量。所以我猜你已经想到了,我们将把已经登录的用户放到g变量里。

我们在调用redirect()时使用的url_for()方法是Flask定义的从给定的view方法获取url。如果你想重定向到index页面,你h很可能使用redirect('/index'),但是我们有很好的理由让Flask为你构造url。

se77en
se77en
翻译于 2013/01/29 10:07
3

当我们从登录表单得到返回数据,接下来要运行的代码也是新写的。这儿我们做两件事。首先我们保存remember_me的布尔值到Flask的session中,别和Flask-SQLAlchemy的db.session混淆了。我们已经知道在一个request的生命周期中用Flask的g对象来保存和共享数据。沿着这条线路Flask的session提供了更多,更复杂的服务。一旦数据被保存到session中,它将在同一客户端发起的这次请求和这次以后的请求中永存而不会消亡。数据将保持在session中直到被明确的移除。为了做到这些,Flask为每个客户端建立各自的session。

se77en
se77en
翻译于 2013/01/29 10:20
3

下面的oid.try_login是通过Flask-OpenID来执行用户认证。这个方法有两个参数,web表单提供的openid和OpenID provider提供的我们想要的list数据项。由于我们定义了包含nickname和email的User类,所以我们要从找nickname和email这些项。

基于OpenID的认证是异步的。如果认证成功,Flask-OpenID将调用有由oid.after_login装饰器注册的方法。如果认证失败那么用户会被重定向到login页面。

se77en
se77en
翻译于 2013/01/29 10:34
2

Flask-OpenID登录回调

这是我们实现的after_login方法(app/views.py)

@oid.after_login
def after_login(resp):
    if resp.email is None or resp.email == "":
        flash('Invalid login. Please try again.')
        redirect(url_for('login'))
    user = User.query.filter_by(email = resp.email).first()
    if user is None:
        nickname = resp.nickname
        if nickname is None or nickname == "":
            nickname = resp.email.split('@')[0]
        user = User(nickname = nickname, email = resp.email, role = ROLE_USER)
        db.session.add(user)
        db.session.commit()
    remember_me = False
    if 'remember_me' in session:
        remember_me = session['remember_me']
        session.pop('remember_me', None)
    login_user(user, remember = remember_me)
    return redirect(request.args.get('next') or url_for('index'))

传给after_login方法的resp参数包含了OpenID provider返回的一些信息。

第一个if声明仅仅是为了验证。我们要求一个有效的email,所以一个没有没提供的email我们是没法让他登录的。

se77en
se77en
翻译于 2013/01/29 10:42
3
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(15)

黑夜的白羊
黑夜的白羊
不用openid登录 怎么实现after_login
A
AsshiDhaka

引用来自“yauchiang”的评论

File "E:\Python\microblog\app\views.py", line 38, in login
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable
这是什么问题呢?我查了http://my.oschina.net/longfirst/blog/355621,g.user.is_authenticated()这个方法返回一个bool值啊?
不要括号 g.user.is_authenticated
谢谢指教
要激活OpenID,yahoo的最好弄!直接去yahoo官网注册,然后挂着你的yahoo-email,再点击login的yahoo-OpenID,就可以登录了!
ZhouLS
ZhouLS

引用来自“yauchiang”的评论

File "E:\Python\microblog\app\views.py", line 38, in login
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable
这是什么问题呢?我查了http://my.oschina.net/longfirst/blog/355621,g.user.is_authenticated()这个方法返回一个bool值啊?

引用来自“楠哥哥大爱小Aom”的评论

同问

引用来自“panweishadow”的评论

flask_login 0.3之后将authenticated从函数更改为属性,你看看你的login版本。参考:http://stackoverflow.com/questions/26313325/typeerror-bool-object-is-not-callable-in-flask
是的。把g.user.is_authenticated() 修改为g.user.is_authenticated就行了
p
panweishadow
我想知道,怎么激活OpenID,这几个哪个好用?
p
panweishadow

引用来自“yauchiang”的评论

File "E:\Python\microblog\app\views.py", line 38, in login
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable
这是什么问题呢?我查了http://my.oschina.net/longfirst/blog/355621,g.user.is_authenticated()这个方法返回一个bool值啊?

引用来自“楠哥哥大爱小Aom”的评论

同问
flask_login 0.3之后将authenticated从函数更改为属性,你看看你的login版本。参考:http://stackoverflow.com/questions/26313325/typeerror-bool-object-is-not-callable-in-flask
楠哥哥大爱小Aom
楠哥哥大爱小Aom

引用来自“yauchiang”的评论

File "E:\Python\microblog\app\views.py", line 38, in login
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable
这是什么问题呢?我查了http://my.oschina.net/longfirst/blog/355621,g.user.is_authenticated()这个方法返回一个bool值啊?
同问
y
yauchiang
File "E:\Python\microblog\app\views.py", line 38, in login
if g.user is not None and g.user.is_authenticated():
TypeError: 'bool' object is not callable
这是什么问题呢?我查了http://my.oschina.net/longfirst/blog/355621,g.user.is_authenticated()这个方法返回一个bool值啊?
楠哥哥大爱小Aom
楠哥哥大爱小Aom
@app.route('/album/<slug>')
def show_album(slug):
r= get_root_dirs_files()
try:
r = r[slug]
except:
return 'Album Not Existing'

return render_template("albums.html", dicts=r)


@app.route("/delete/<path:object_name>")
def delete(object_name):
obj = storage.get(object_name)
print obj
remove('./static/uploads/Pictures/%s' % (obj.name))
return redirect(url_for('show_album'???))


delete函数中,url_for show_album 函数时,里面的参数要怎么填?
zypy333
zypy333
注意下,现在引入要改成
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
返回顶部
顶部