原文: http://www.catonlinepy.tech/
声明: 原创不易,未经许可,不得转载
1. 你将学会什么
今天的课程主要给大家介绍一款非常好用的 Flask 插件—Flask_Login,该插件主要用来管理用户的登陆状态。通过今天的学习,你将学会如何维护用户的登录、登出状态。教程中的代码都会托管到 github 上,猫姐一再强调,在学习本课内容时一定要亲自动手实现代码,遇到问题再到 github 上查看代码,如果实在不知道如何解决,可以在日志下方留言。
2. 使用 Flask_Login
2.1 项目目录结构的创建
当用户登录某个应用时,应用需要记住该用户的登陆状态。在开发过程中,如果我们自己造轮子,去实现管理用户登录状态的代码,就会浪费大量时间。但现实中,我们往往需要聚焦在业务上的开发,Flask_Login 就是前人造好的管理用户登录状态的轮子,并且这个插件上手起来毫无难度。照旧,在正式使用 Flask_Login 插件之前,还是先建立今天的项目目录,如下:
# 进入到虚拟环境目录,激活虚拟环境
maojie@Thinkpad:~/flask-plan/$ source miao_venv/bin/activate
# 到 flask-course-primary 目录下创建第五天的课程 day5 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ mkdir day5
# 进入 day5 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ cd day5
# 新建 userauth_demo 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ mkdir userauth_demo
# 进入到 userauth_demo 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ cd userauth_demo/
# 在 userauth_demo 目录中新建__init__.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch __init__.py
# 在 userauth_demo 包中新建 routes.py 路由文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch routes.py
# 在 userauth_demo 包中新建 models.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch models.py
# 在 userauth_demo 包中新建 forms.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch forms.py
# 在 userauth_demo 包中新建 config.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ touch config.py
# 在 userauth_demo 包中新建 templates 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ mkdir templates
# 进入到 templates 目录
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo$ cd templates/
# 在 templates 目录中新建 layout.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch layout.html
# 在 templates 目录中新建 login.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch login.html
# 在 templates 目录中新建 register.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch register.html
# 在 templates 目录中新建 index.html
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/userauth_demo/templates$ touch index.html
# 在 day5 目录下新建 run.py 文件
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5/$ touch run.py
最终,我们得到今天项目的目录结构如下(使用 tree 命令得到):
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary$ tree day5
day5
├── run.py
└── userauth_demo
├── config.py
├── database.db
├── forms.py
├── __init__.py
├── models.py
├── routes.py
└── templates
├── index.html
├── layout.html
├── login.html
└── register.html
安装 Flask_Login 插件的方式与其它插件的安装方式一样,使用 pip 命令,如下:
# 注意,一定要在虚拟环境中
(miao_venv) maojie@Thinkpad:~/flask-plan/flask-course-primary/day5$ pip install Flask_Login
然后在__init__.py 文件中对 Flask_Login 进行配置,大家首先把第 4 天__init__.py 文件中的内容复制过来,然后添加如下代码:
# ...
from flask_login import LoginManager
app = Flask(__name__)
# ...
login_manager = LoginManager(app)
# ...
2.2 准备用户的登陆模型
在正式创建登录模型之前,我们先介绍一下 config.py 文件,该文件的作用存放与应用相关的所有配置信息。在之前的教程中,我们直接将配置信息写到了__init__.py 文件中,这样不利于复杂 web 应用程序的开发管理,后续章节的配置内容都将会放到 config.py 文件中,配置代码如下所示:
# config.py 文件中的内容
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config(object):
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'database.db')
# 使用表单, 对 Flask_WTF 进行配置
SECRET_KEY = 'miaojie is great!'
__init__.py 文件只需添加如下代码便可导入 config.py 文件中所有配置信息:
# __init__.py 文件中的内容
# ..
from userauth_demo.config import Config
app = Flask(__name__)
app.config.from_object(Config)
# ..
建立用户的登陆模型是创建数据库表的过程,在第 4 课中,我们谈到了如何去建立用户模型,这里不再详细讲解,直接使用即可。要想使用 Flask_Login 插件,在模型中需要实现下面几个方法 (函数),如下:
方法 说明
is_authenticated() 如果用户已经登录, 必须返回 True , 否则返回 False
is_active() 如果允许用户登录, 必须返回 True , 否则返回 False。如果要禁用账户, 可以返回 False
is_anonymous() 对普通用户必须返回 False
get_id() 必须返回用户的唯一标识符, 使用 Unicode 编码字符串
但是 Flask_Login 提供了一个更简便的方法—UserMixin 类,开发人员只需要让 User 类继承 UserMixin 类即可完成对 User 表的登录管理,UserMixin 包含这 4 个方法的默认实现(因此上面这张表的内容大家完全不用关心)。由于后面的代码会越来越复杂,为了使代码更易管理,所有建立数据库模型的代码将放在 models.py 文件中。如下所示,在 models.py 中建立 User 模型:
# 在 models.py 文件中的内容
#!coding:utf8
from userauth_demo import db
from flask_login import UserMixin
# User 继承 UserMixin 类
class User(UserMixin, db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}','{self.email}','{self.password}')"
# 在这个模型中,还添加了 email 字段,因为在用户登陆的时候,不仅可以输入用户名登陆,也可以输入 email 登陆
第 4 课中已经讲了数据库的迁移,大家可以直接按照第 4 课的 5.3 小结内容进行操作即可,同样在操作之前,需要设置环境变量,环境变量在第 1 课的 3 节最后已经讲过如何设置了。
最后,Flask_Login 插件要求程序有一个回调函数,可以加载指定 ID 的用户。在 models.py 文件中增加如下代码:
# ..
# 从 userauth_demo 包中导入 login_manager 实例
from userauth_demo import login_manager
# 回调函数
@login_manager.user_loader
def load_user(user_id):
# print 调试时用
#print(user_id)
return User.query.get(int(user_id))
# ..
2.3 添加登陆表单
表单在第三课中已经讲到过,现在将表单直接拿过来用,所有建立表单的代码都将放在 forms.py 文件中,在 forms.py 文件中添加登陆表单内容:
# 在 forms.py 文件中的内容
from flask_wtf import FlaskForm
from wtforms.fields.html5 import EmailField
from wtforms import PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Length
class LoginForm(FlaskForm):
# 表单包括邮箱,密码和是否记住密码及一个提交按钮
email = EmailField(u'邮箱', validators=[DataRequired(), Length(min=2, max=20)])
password = PasswordField(u'密码', validators=[DataRequired()])
remember = BooleanField(u'记住我')
submit = SubmitField(u'登陆')
在表单中 email 字段使用了 WTForms 提供的 Length()验证函数,确保用户输入的 email 长度在 2 到 20 的范围内,PasswordField 可使 Jinja2 生成 type=”password” 的 <input> 标签;BooleanField 可使 Jinjia2 生成 true 或 false 的可选框;SubmiteField 可使 Jinjia2 生成 type=”submit” 的 <input> 标签。
2.4 表单的渲染
在第二课中已经讲到过模板的继承概念,今天的课程会继续用到这一概念,这里就不重复描述了(大家如果不清楚,可回去再看看第 2 天的课程)。在 templates/layout.html 中输入如下 html 代码:
<!-- layou.html 文件中的内容 -->
<html>
<head>
{% if title %}
<title>{{title}}- 喵星在线 </title>
{% else %}
<title> 喵星在线 </title>
{% endif %}
</head>
<header>
<div>
<a href="/"> 主页 </a>
<a href="/login"> 登陆 </a>
</div>
</header>
<body>
<!-- 渲染 flash 消息 -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{category}}">
{{message}}
</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %}
{% endblock %}
</body>
</html>
然后通过子模板 index.html 继承基模板 layout.html,在 templates/index.html 中输入如下代码:
<!-- index.html 文件中的内容 -->
{% extends "layout.html" %}
<!-- 重新填写 content 块的内容 -->
{% block content %}
<h1> 你好,喵星在线!</h1>
{% endblock %}
此外,我们还需要一个登录页面,让我们可以输入邮箱和密码,在 templates/login.html 中输入如下代码:
<!-- login.html 文件中的内容 -->
{% extends "layout.html" %}
{% block content %}
<h2> 登陆 </h2>
<form action=""method="post">
{{html_form.hidden_tag() }}
<div>
{{html_form.email.label}}<br />
{{html_form.email() }}
{% if html_form.email.errors %}
<div>
{% for error in html_form.email.errors %}
<span>{{error}}</span>
{% endfor %}
</div>
{% endif %}
</div>
<div>
{{html_form.password.label}}<br />
{{html_form.password() }}
</div>
<div>
{{html_form.remember.label}}
{{html_form.remember() }}
</div>
<div>
{{html_form.submit() }}
</div>
</form>
{% endblock content %}
上面已经完成 login.html 页面前台 form 表单的显示(渲染)工作,这时就需要在视图函数中(python 文件)将代表表单的类传递到前端模板文件(html 文件)中,下面在 routes.py 中完成视图函数的编写:
# routes.py 文件中的内容
#!coding:utf8 # 文件中有中文,必须在代码最前面注明 utf8 编码,否则程序拉起时会报错
from flask import render_template, redirect, url_for, flash
from userauth_demo import app
from userauth_demo.forms import LoginForm # 从 userauth_demo.forms 中导入 LoginForm 类
from flask_login import current_user
from userauth_demo.models import User
# 添加视图函数渲染主页内容
@app.route('/')
def index():
return render_template('index.html', title='第五天')
# 在视图函数中将 form 表单传到前端 html 文件中去
@app.route('/login', method=['GET','POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
# 对类 LoginForm 的实例
form = LoginForm()
if form.validate_on_submit():
if form.email.data == '424277539@qq.com' and form.password.data == 'miaojie':
flash('你已经登陆成功', 'success') # flash 消息的渲染
return redirect(url_for('index')) # 如果登陆成功,则重定向到主页
# 将视图函数中的变量 form 传到模板 login.html 文件中去
return render_template('login.html', title='第五天', html_form=form)
current_user 是由 Flask_Login 导入的,它在视图函数和模板中可以直接使用。它表示的是当前登陆的用户,如果用户未登陆,则表示用户是匿名用户,is_authenticated 则会返回 False。当在表单中填好内容后,点击提交按钮时,validate_on_submit 发送的是 post 请求,if 判断语句中填入的是假数据,因为数据还没有写入到数据库,无法使用数据库将用户的邮箱和密码读取出来,后面会讲到用户的注册,将用户的信息写到数据库中。
在 run.py 文件中输入如下代码,将 web 程序拉起:
from userauth_demo import app # 从 userauth_demo 包中导入 app 实例
if __name__ == "__main__":
app.run(debug=True, port=5005) # app 实例调用自己的 run 函数
# 在 app.run 里面可以指定端口号, 当下次再运行 python run.py 时,可能提示端口号被占用,此时,可以修改 run.py 文件中的端口号
现在,所有代码已编写完成,通过 python run.py 将 web 程序拉起,开始验证结果,在浏览器中输入 http://127.0.0.1:5005/login,效果图如下所示:
在登陆表单中输入用户名及密码,点提交按钮后的效果图:
3. 上篇完,未完待续 …
由于第 5 天的课程涉及登录和注册两方面的知识,内容比较多,因此猫姐将第 5 天的课程分为上、下两篇进行讲解,这里我们完成上篇登录功能的实现,下篇我们将接着讲解注册功能,以及注册后登录功能的实现。