在Flask项目中可以用Blueprint(蓝图)实现模块化的应用,使用蓝图可以让应用层次更清晰,开发者更容易去维护和开发项目。蓝图将作用于相同的URL前缀的请求地址,将具有相同前缀的请求都放在一个模块中,这样查找问题,一看路由就很快的可以找到对应的视图,并解决问题了。
application
users模块
vote模块
report模块
…
这些模块使用Blueprint可以拥有自己的url前缀地址进行区分
# 模块内 views.py文件
from flask import Blueprint
users = Blueprint('users', __name__)
@users.route('/register')
def register():
return '注册页面'
# application/__init__.py
# 放在db = SQLAlchemy(app)这条语句后面
from application.users.views import users
# 注册蓝图
app.register_blueprint(users, url_prefix='/users')
在application下创建templates目录, 分模块存放页面,继承base.html
安装
$ pip install flask-wtf
from flask_wtf import FlaskForm
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms import ValidationError
from wtforms.validators import DataRequired, Length
from application.users.models import Users
class RegisterForm(FlaskForm):
username = StringField('用户名', validators=[ DataRequired(), Length(max=128) ])
password = PasswordField('密码', validators=[ DataRequired(), Length(min=6, message='密码不能小于6位.') ])
fullname = StringField('姓名', validators=[ DataRequired(), Length(max=128) ])
submit = SubmitField('提交')
# 验证用户名是否存在
def validate_username(self, field):
if Users.query.filter_by(username=field.data).first():
raise ValidationError('用户名已存在.')
用户登录程序后,他们的认证状态要被记录下来,这样浏览不同的页面时才能记住这个状 态。Flask-Login 是个非常有用的小型扩展,专门用来管理用户认证系统中的认证状态,且不依赖特定的认证机制。
$ pip install flask-login
要想使用 Flask-Login 扩展,程序的 Users 模型必须实现几个方法
is_authenticated()
如果用户已经登录,必须返回True,否则返回False
is_active()
如果允许用户登录,必须返回 True,否则返回 False。如果要禁用账户,可以返回 False
is_anonymous()
对普通用户必须返回 False
get_id()
必须返回用户的唯一标识符,使用 Unicode 编码字符串
这 4 个方法可以在模型类中作为方法直接实现,不过还有一种更简单的替代方案。Flask- Login 提供了一个 UserMixin 类,其中包含这些方法的默认实现,且能满足大多数需求。
from flask_login import UserMixin
from application import db
import datetime
# 用户表
class Users(UserMixin, db.Model):
__tablename__ = 'users'
# 主键
id = db.Column(db.Integer, primary_key=True)
# 用户名 唯一索引
username = db.Column(db.String(128), unique=True, nullable=False)
# Hash密码 必填字段
password = db.Column(db.String(512), nullable=False)
# 姓名 创建索引,加快查询
fullname = db.Column(db.String(128), index=True, nullable=False)
# 状态 (1: 生效 0: 禁用)
status = db.Column(db.SmallInteger, default=1, nullable=False)
# 创建时间 默认当前时间
created_time = db.Column(db.DateTime, nullable=False, default=datetime.datetime.now, index=True)
def __repr__(self):
return 'username=%s' % self.username
Flask-Login 在application/init.py中初始化
from flask_login import LoginManager
# 初始化LoginManager
login_manager = LoginManager()
# 设为 'strong' 时,Flask-Login 会记录客户端 IP 地址和浏览器的用户代理信息,如果发现异动就退出用户
login_manager.session_protection = 'strong'
# login_view 属性设置登录页面
login_manager.login_view = 'users.login'
login_manager.init_app(app)
LoginManager 对象的 session_protection 属性可以设为 None、’basic’ 或 ‘strong’,以提 供不同的安全等级防止用户会话遭篡改。设为 ‘strong’ 时,Flask-Login 会记录客户端 IP 地址和浏览器的用户代理信息,如果发现异动就退出用户。login_view 属性设置登录页面 的端点。
Flask-Login 要求程序实现一个回调函数,使用指定的标识符加载用户。
from application import login_manager
@login_manager.user_loader
def load_user(user_id):
return Users.query.get(int(user_id))
为了保护路由只让认证用户访问,Flask-Login 提供了一个
login_required 装饰器。用法演示如下:
from flask_login import login_required
@app.route(‘/secret’)
@login_required
def secret():
return '只有登录用户可以看'
如果未认证的用户访问这个路由,Flask-Login 会拦截请求,把用户发往登录页面
# 验证成功,登录用户
login_user(user, remember=form.remember_me)
login_user() 函数的参数是要登录的用户,以及可选的“记住我”布 尔值,“记住我”也在表单中填写。如果值为 False,那么关闭浏览器后用户会话就过期 了,所以下次用户访问时要重新登录。如果值为 True,那么会在用户浏览器中写入一个长 期有效的 cookie,使用这个 cookie 可以复现用户会话。
判断条件中的变量 current_user 由 Flask-Login 定义,且在视图函数和模板中自动可用。 这个变量的值是当前登录的用户,如果用户尚未登录,则是一个匿名用户代理对象。如果 是匿名用户,is_authenticated() 方法返回 False。所以这个方法可用来判断当前用户是否 已经登录。
总体步骤
搭建一个Flask package的框架(参见第二天的框架)
school(项目目录)
application (package)
__init__.py
views.py
models.py
manager.py
演变为Blueprint结构
school(项目目录)
application (项目package)
__init__.py
users(模块package)
__init__.py
models.py
views.py
forms.py
home(模块package)
__init__.py
...
templates(项目模板)
users(模板目录)
home(模板目录)
...
static
js
css
images
manager.py
在各模块的views.py里面生成Blueprint对象,在application/__init__.py
里面注册各模块的Blueprint对象和url前缀
在views.py里面编写使徒的方法
注册功能 def register
在templates里面编写对应的模板页register.html(Jinja2)
创建母版页layout.html,为其他页面实现公用
新建forms.py(Flask-WTF), 根据表单增加相应的字段,字段加入校验的规则,验证用户是否存在可以加在forms.py里面的(validate_user(field名))
在views.py里面创建form对象,并将form对象传给页面
页面是渲染表单,显示隐藏标签(form.hidden_tag)->csrf_token
form.username.lable :生成用户 form.username:生成输入框
form.submit
在整个过程中使用url_for(‘xxx.xxx’)生成url ,xxx.xxx是模块views.py中的方法名
在views.py方法上面如果是get和post方法,需要在路由中加入methods=[‘GET’, ‘POST’]
form.validated_on_submit()进行表单验证,如果验证失败,在页面显示错误信息,使用法flash()推送通用整体消息到页面,各字段的错误(form.username.errors),使用模板macro定义了的通用错误信息的方法,macros.html,在layout里面import macros.html,页面可以通用
如果表单验证通过,生成Users对象,password做了generate_password_hash,db.session.add(user),db.session.commit(),return redirect(‘url_for()’)
登录模块,生成表单一样的步骤;user = Users.query.filter_by(username=username).first()
,验证密码是否正确使用check_password_hash(password_hash, password)
使用Flask_Login模块里面的login_user方法自动登录用户,编写了load_user回调函数(从数据库中加载当前登录的对象),可以通过current_user拿到当前登录用户,用current_user.is_authenticated()判断用户是否登录