什么是Flask ?
Flask是一个使用Python编写的轻量级Weby应用框架。其WSGI工具箱采用Werkzeug,模板引擎则使用JInja2.除了Werkzeug 和Jinjia2以外几乎不依赖任何外部库,因为Flask被称为轻量级框架。Flask使用签名cookie来允许用户查看和修改会话内容,他会记录从一个请求到另一个请求的信息 ,但是如果要修改会话,则必须有秘钥Flask.secret_key。
1.浏览器和服务器的关系
1)浏览器:浏览器指的是电脑、手机或者平板上的一个程序。它发送请求,接收响应,展示页面
2)服务器,是一个笼统的概念,一般来说有2种理解
硬件:一台电脑(真正的服务器配置往往较高)
软件:提供html、css、js、图片、视频、音频等的一个程序
补充:
在开发web相关的程序时,浏览器就是一个展示页面的程序,服务器就是一个提供页面数据的程序
如果在部署(就是真正的让编写的网站能够被用户访问)时,服务器往往值得是那台电脑
2.流程
1)接收浏览器发送过来的请求
不同的请求意味着不同的url,例如:
登录时:http://xxx.com/login.html
注册时:http://xxx.com/register.html
2)定义不同的函数,每个url都对应一个独特的函数,这个函数进行逻辑处理
例如http://xxx.com/login.html 对应 一个函数叫做def login():
例如http://xxx.com/register.html 对应一个函数叫做def register():
3)在函数中处理具体的逻辑,例如:
数据库的操作
用户验证、cookie、Session等操作
4)返回一个完整的响应数据,例如:
返回一个HTML页面的数据
2.开发Flask的准备工作
创建虚拟环境
mkvirtualenv 虚拟环境的名字 -p python3
workon 虚拟环境的名字
3.flask第1个页面的解说
1)from flask import Flask
导入Flask类,flask对象需要,flask对象是这个web框架的核心
2)app = Flask(__name__)
Flask函数接收一个参数__name__它会指向程序所在的包
3)Flask用这个参数决定程序的目录,以便稍后能够找到相对于程序根目录的资源文件位置
4)@app.route("/")是一个带有参数的装饰器,其作用是将路由映射到视图函数 index
5)app.run()
Flask应用程序实例的run方法 启动 WEB 服务器,这个web服务器是flask自带的,仅用来开发调试使用
4.返回html页面数据
视图函数的作用主要是逻辑处理,但是还有一个功能,就是将url对应的页面数据返回给浏览器,这样浏览器才能看到一个完整的页面
即视图函数中,return返回的'字符串',浏览器就会当做html代码进行渲染,利用这个特点,就可以把之前学习过的前端html的知识进行应用了,只要有一个完整的html代码,然后将其当做字符串用return返回,那么浏览器就可以看到了
升级profile视图函数
此时可以单独定义一个html文件,在视图函数中用open打开就可以
5.路由 视图 模板
路由:
1)指定路由地址
如果需要让浏览器可以访问一个新的url,那么需要添加一个路由
路由的地址确定,那么就确定了浏览器访问时的url
函数的名字可以任意,只要不重复就行
2)给路由传参
有时我们需要将同一类 URL 映射到同一个视图函数处理,比如:使用同一个视图函数来显示不同用户的个人信息。
3)总结:
路由的作用:规定url对应的函数是哪个
浏览器中访问的路由是定义时规定的那个,而不是函数的名字
在浏览器的url中可以传递用户的标记信息,从而让函数中知道用户的意图,从而能完成相应的效果
浏览器访问web服务器的过程到现在应该明白了,如果感觉生疏,那么只需要多练习几次就可以了
视图:
1)那个处理的“函数”,那个函数中可以通过return将一个字符串返回,那么浏览器就可以看到访问指定的url时需要的页面数据
我们把那个"函数"称之为 视图(视图函数)
通俗的说:这个视图函数就是用来处理某个url的逻辑的
2)返回的数据
返回 JSON
重定向函数url_for
自定义状态码
2.1返回JSON:
在使用 Flask 写一个接口时候需要给客户端返回 JSON 数据,在 Flask 中可以直接使用 jsonify 生成一个 JSON 的响应
注意:不推荐使用 json.dumps 转成 JSON 字符串直接返回,因为返回的数据要符合 HTTP 协议规范,如果是 JSON 需要指定 content-type:application/json
2.2重定向
例:重定向到 baidu 官网
重定向到自己写的视图函数
可以直接填写自己 url 路径
也可以使用 url_for 生成指定视图函数所对应的 url
重定向到带有参数的视图函数
在 url_for 函数中传入参数
模板
Jinja2模板引擎简介
视图函数的主要作用是生成请求的响应,这是最简单的请求。
实际上,视图函数有两个作用:
处理业务逻辑
返回响应内容
在大型应用中,把业务逻辑和表现内容放在一起,会增加代码的复杂度和维护成本。
本节学到的模板,它的作用即是承担视图函数的另一个作用,即返回响应内容。
模板其实是一个包含响应文本的文件,其中用占位符(变量)表示动态部分,
告诉模板引擎其具体的值需要从数据中获取
使用真实值替换变量,再返回最终得到的字符串,这个过程称为“渲染”
Flask是使用 Jinja2 这个模板引擎来渲染模板
#使用模板的好处:
a.视图函数只负责业务逻辑和数据处理(业务逻辑方面)
而模板则取到视图函数的数据结果进行展示(视图展示方面)
b.代码结构清晰,耦合度低
#Jinja2 两个概念:
Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板引擎
模板语言:是一种被设计来自动生成文档的简单文本格式
在模板语言中,一般都会把一些变量传给模板,替换模板的特定位置上预先定义好的占 位变量名
渲染模版函数
Flask提供的 render_template 函数封装了该模板引擎 render_template 函数的第一个参数是模板的文件名,后面的参数都是键值对,表示模板中变量对应的真实值。
{{}} 来表示变量名,这种 {{}} 语法叫做变量代码块
<h1>{{ user_id }}</h1>
例:
@app.route("/profile_v4/<user_id>")
def profile_v4(user_id):
return render_template("profile.html")
6.flask使用数据库-基础
1)只需要知道用户的标记,例如qq993484988即可,然后拿着这个用户的标记信息,到数据库中查询出于这个用户所需要的信息,例如:家庭地址,,,再然后将这些数据传递到render_template函数中让它替换到合适的html数据中,这样就完美解决了
2)数据库准备
2.1. 新建一个数据库
create database flask_1 charset=utf8;
2.2. 新建立一个数据表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(50) NOT NULL,
`user_name` varchar(50) NOT NULL,
`head_img` varchar(200) DEFAULT NULL,
`short_description` varchar(300) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
2.3. 导入数据
insert into `flask_1`.`user` (
`head_img`, `user_id`, `short_description`, `user_name`
)
values (
'993484988.jpeg', '993484988', '我就是老王,你不懂我,但我熟掌你心,哈哈哈哈 哈', '老王'
);
2.4. 查询所有数据
select * from user;
3)在视图函数中添加对数据库的操作
from flask import Flask
from flask import render_template
import pymysql
#创建flask对象
app = Flask(__name__)
@app.route("/profile_v5/<user_id>")
def profile_v5(user_id):
# 1. 查询数据库
# 1.1 创建Connection连接
conn = pymysql.connect(host='localhost', port=3306, database='flask_1', user='root', password='123456', charset='utf8')
# 1.2 获得Cursor对象
cs1 = conn.cursor()
# 1.3 构造参数列表
params = [user_id]
# 1.4 执行select语句,并返回受影响的行数:查询所有数据
cs1.execute('select * from user where user_id=%s', params)
# 1.5 获取查询的结果
result = cs1.fetchone()
# result = cs1.fetchall()
print(result)
# 1.6 关闭Cursor对象
cs1.close()
# 1.7 闭Connection对象
conn.close()
# 2. 模板渲染
return render_template("profile.html", xxxx=user_id)
if __name__ == '__main__':
app.run()
7.flask使用数据库-SQLAlchemy
1)之前添加python原生操作MySQL的方式,来实现查询数据。
SQL语句有很多,而且很容易出错,一丁点的地方写错了,那么意味着这个SQL语句就有问题,如果在开发的过程中经常因为SQL语句的小问题,而耽误了项目的整体进度
就是定义一个类,对这个类的对象操作,最终它自动转化为SQL语句,就是SQLAlchemy
2)使用SQLAlchemy对项目进行升级
2.1. 定义类模型
from flask import Flask
from flask import render_template
import pymysql
#创建flask对象
app = Flask(__name__)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, String, Integer
from sqlalchemy.orm import sessionmaker
#链接是需要指定要用到的MySQL数据库
engine = create_engine('mysql+pymysql://root:123456@localhost:3306/flask_1?charset=utf8')
Base = declarative_base() # 生成SQLORM基类
class User(Base):
# 对应MySQL中数据表的名字
__tablename__ = 'user'
# 创建字段
id = Column(Integer, primary_key=True)
user_id = Column(String(50), nullable=False)
user_name = Column(String(50), nullable=False)
head_img = Column(String(200))
short_description = Column(String(300))
@app.route("/profile_v8/<user_id>")
def profile_v8(user_id):
# 1. 查询数据库
# 创建session对象
DBSession = sessionmaker(bind=engine) # 创建与数据库的会话,返回的是一个类
session = DBSession() # 生成链接数据库的实例
# 获取返回数据的第一行
obj = session.query(User).filter(User.user_id == user_id).one()
# 关闭session
session.close()
# 2. 模板渲染
return render_template("profile.html", user_name=obj.user_name, head_img=obj.head_img, short_description=obj.short_description)
if __name__ == '__main__':
app.run()
升级项目
将模型类放到models包中
蓝图
1.前台、后台、前端、后端
前台:就是普通用户访问一个网站时,能够看到的页面(一句话:给老百姓看到的)
后台:提供给系统管理者能够看到的页面,而普通用户看不到的页面(一句话:给网站运营人员看数据的)
前端:对于程序员而言,在进行编程的时候,那些用来呈现效果的代码一句话:由html/css/js组成的代码
后端:对于程序员而言,进行编程的时候,那些用来不是给用户看,而是处理业务的那些代码一句话:由Python、PHP、java、go等语言编写的操作数据库等其他业务处理的代码
2.后台的显示
发现问题
views/admin/views.py中没有app这个对象
感觉main.py与views/admin/views.py隔得很远,不知道怎样调用,即将无法将它们关联在一起
解决
步骤1:在views/admin/__init__.py文件中定义个蓝图
步骤2:用这个蓝图替代views/admin/views.py中的app,从而不会因为没有app而失败
步骤3:在main.py中注册步骤1的那个蓝图,这样app就与蓝图进行了关联,而蓝图又与视图函数进行了关联
#代码
views/admin/__init__.py
from flask import Blueprint
#创建一个蓝图,用来管理多个函数视图
admin_blu = Blueprint("admin", __name__)
from . import views
views/admin/views.py
from flask import render_template
from sqlalchemy.orm import sessionmaker
from models.models import User, engine
from . import admin_blu
@admin_blu.route("/user_info")
def user_info():
# 创建session对象
DBSession = sessionmaker(bind=engine) # 创建与数据库的会话,返回的是一个类
session = DBSession() # 生成链接数据库的实例
# 查询user表中所有的用户数据
all_user_info = session.query(User).all()
print(all_user_info)
# 关闭session
session.close()
return render_template("index.html", user_infos=all_user_info)
main.py
from flask import Flask
from views.admin import admin_blu
app = Flask(__name__)
# 注册蓝图
app.register_blueprint(admin_blu)
if __name__ == '__main__':
app.run()
注册蓝图时,添加前缀
为了更好的区分出,前后台,url中一般添加前缀,这样有利于管理
Cookie、Session
cookie
1.指某些网站为了辨别用户身份、进行会话跟踪,在用户浏览器中存储的数据(通常经过加密)
2.在访问网站时如果通过了身份验证(判断用户名、密码),就将一个标记数据(就是cookie)存储在浏览器中
以后每次浏览器访问上一步认证通过的那个网站,就会自动携带这个标记
服务器检查是否有这个标记,从而判断是否为这个请求提供服务
3.Cookie是由服务器端生成,发送给客户端浏览器,浏览器会将Cookie的key:value保存,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)
Cookie的key:value可以由服务器端自己定义
示例
1)最典型的应用是判定注册用户是否已经登录网站,用户可能会得到提示,是否在下一次进入此网站时保留用户信息以便简化登录手续,这些都是Cookie的功能
2)网站的广告推送。经常遇到访问某个网站时,会弹出小窗口,展示我们曾经在购物网站上看过的商品信息
3)购物车。用户可能会在一段时间内在同一家网站的不同页面中选择不同的商品,这些信息都会写入Cookie,以便在最后付款时提取信息。
提示:
1)Cookie是存储在浏览器中的一段纯文本信息 ,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用
2)当浏览器请求某网站时,会将本网站下所有Cookie信息提交给服务器,所以在request中可以读取Cookie信息
3)Cookie基于域名安全,不同域名的Cookie是不能互相访问的
4如访问howdoit.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息,浏览器不会向不同的域名提交其他域名下的cookie
cookie的研究
使用网络调试助手,充当tcp服务器,然后用谷歌浏览器充当tcp客户端,然后浏览器向服务器发起请求,就可以和清除的在网络调试助手中看到http的请求头,然后通过网络调试助手回送http响应头+响应体
在响应头中通过设置Set-Cookie从而向浏览器中设置想要的数据,这些数据其实就是cookie
http响应头设置cookie的方式:
Set-Cookie: cookie-name=cookie-value;
用cookie 标记的实例
结果为:
详情页面
总结
cookie存储在浏览器中,用来记录某个特殊的数据,等下次登录同一个网站时,浏览器会自动提交,从而flask能够判断这个请求是否拥有权限访问对应的页面
设置过期时间
request介绍
request 是flask中代表当前请求对象
在视图函数中直接使用可以取到当前本次请求的所有数据,包括url中的参数、header、body等
退出登录-cookie方式
1.退出登录原理
将这个cookie给清理掉即可,但是cookie毕竟不是在服务器上存储的,所以我们需要通过http的响应头的方式告诉浏览器将对应的cookie删除
2. 操作流程分析
1)在profile.html页面中添加一个超链接,即当用户点击时,就要发其一个新的url请求
2)在上一步中url对应的视图函数中,通过set_cookie得到一个特殊的http响应头
3)将上一步的响应返回给浏览器
4)浏览器自动清理对应的cookie
3)代码实现
伪造cookie值
刷新后
总结
cookie是采用key:value的方式,将很多服务器对用户的数据存储到了浏览器中
将很多重要的标记信息存储在用户的浏览器中是很危险的
Session
1)cookie的漏洞问题,有的同学可以想到,可以进行加密,即在设置cookie的时候将user_id等信息进行加密,此时浏览器再进行伪造时,就比较麻烦了
这个方式是可行的,但是试想下:如果cookie中存储了多个信息,例如user_id、user_email、user_tel等信息时,那么每个信息都加密,那么当web服务器接收到之后肯定需要解密,服务器的cpu就会被占用;
简言之:如果将常用的信息,通过cookie的方式存储到浏览器中,即使是将信息进行加密,也不是一种很合适的解决方案
有一种现在被各大互联网公司使用的方式是,session
2)Session
如果将user_id、user_email、user_tel等信息存储在web服务器中,然后用一个加密的hash值,当做cookie存储到浏览器中,此时:
浏览器中只有一个cookie
用这个cookie值当做key在web后端进行缓存的查询
这样就是现在的常用解决方案
session通俗总结:
是缓存用用户信息且尽量避免被伪造的一种存储方式
特点是将很多信息都存储到了web服务器端,为了能够让不同的浏览器访问的时候,还能够找到对应的自己的数据,依然需要使用cookie,只不过cookie值此时只需要一个特殊值(一般是进行加密的值)即可
登录页面设置Session
只设置Session 还不行 还要配置使用session
时配置的密钥
详情页面从session中取出数据
可以看到,后端中存储了2个数据,login_flag、user_id,而前端浏览器中只有一个cookie值
注意:
flask默认的session机制是将需要存储到session中的数据按照base64进行加密,然后将解决又存储到了cookie中
其实这是为了flask方便使用,它默认才使用的这种方式,但是一定要注意 实际工作中肯定不用这方方式
后面会讲解到将session存到到Redis数据库,不要着急,先了解即可
退出登录
1.正常情况下,关闭浏览器不会将cookie等信息清除,如果真的要清除:
1)浏览器当达到一定缓存量之后,自动清除
2)手动进行清除
2.如果要真正的退出登录,目的只有1个:清除浏览器中缓存的cookie信息
问题是,总不能每次都需要手动清除cookie来退出吧,因此一个完整的网站中,需要有退出的功能
直白点说:在大部分的页面中有个超链接,这个超链接连接到一个视图函数,这个视图函数中有清除session的功能
3. 实现
html
页面上显示的推出按钮
后端代码的实现
再打开浏览器 没有看到任何的Cookie 信息 说明清楚成功
session和cookie的区别
-
cookie、session作用
1)由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。 2)思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。 3)Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。
-
总结
1)Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个 数据可以保存在集群、数据库、文件中 2)Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式 3)对于敏感、重要的信息,建议要存储在服务器端,不能存储在浏览器中,如用户名、余额、等级、验证码等信息 4)在服务器端进行状态保持的方案就是Session 5)Session依赖于Cookie
POST、AJAX、JSON
POST
1.post是啥
大家应该知道浏览器与服务器之间是依靠HTTP协议进行传输的
HTTP协议有分为
请求(request)
响应(response)
在请求、响应中都由2部分组成:
头(head)
体(body)
其中请求时的head,标记了很多重要的信息,例如
url
http协议版本
cookie等
在标记URL时,我们之前见过的都是GET,其实除了GET之外,还有很多,例如常见的:
POST
PUT
DELETE
。。。等
无论是GET还是POST,其实就是HTTP协议中的一部分,一般情况下,
GET:往往用来表示从服务器获取数据
POST:往往用来向服务器提交数据
总结
http在传送数据的时候有很多种方式,其中常用的是GET和POST
GET与POST不同点最直接的体现在:
GET:放到URL中,又因为不同的浏览器对URL支持的长度不同,向谷歌浏览器URL最大长度限制为8182个字符,所以不能放入太多的数据
POST:放到body中,body的长度不限,所以可以提交大量的数据
在以后的学习、工作中,如果要获取数据,例如获取某个页面,那么使用GET方式
如果要提交数据,则建议使用POST
AJAX
1.什么是ajax
通俗的说:在一种请求方式,这种方式可以"背后"与服务器沟通,当服务器返回数据后,由js来操作这些数据改怎样使用,可以将这些数据替换到某个标签中,也可以做任何想要做的事情;它最大的特点,不像在浏览器输入网址的那个位置,只要输入一个网址,回车之后整个页面刷新;ajax可以做到部分刷新
2.代码:
$("#login").click(function(){
$.ajax({
url:"http://xxx.com/login",
type:"post",
data:{
username:"shangsan",
password:"123456"
},
success:function(data){
// 这里是访问成功时被自动执行的代码
if(data.errorCode==0){
。。。。
}else{
。。。。
}
},
error:function(jqXHR){
// 这里是访问失败时被自动调用的代码
}
});
}
JSON
1.什么是JSON
JSON 是一种轻量级的基于文本的开放标准,被设计用于可读的数据交换。可以被众多编程语言所使用:包括 C,C++,Java,Python,Perl 等等=
JSON 是 JavaScript Object Notation 的缩写
被设计用于可读的数据交换
它是从 JavaScript 脚本语言中演变而来
JSON 的网络媒体类型是 application/json
2.JSON 使用范围
JSON 格式可以用于通过网络连接序列化和传输结构化数据
主要用于在服务器和 Web 应用程序之间传输数据
3.JSON 特点
JSON 容易阅读和编写
它是一种轻量级的基于文本的交换格式
语言无关
4 JSON 简单示例
{
"book": [
{
"id":"01",
"language": "Java",
"edition": "third",
"author": "Herbert Schildt"
},
{
"id":"07",
"language": "C++",
"edition": "second"
"author": "E.Balagurusamy"
}]
}
5.Python的字典和JSON有什么区别
Python 的字典是一种数据结构,JSON 是一种数据传输格式
json 就是一个根据某种约定格式编写的纯字符串,不具备任何数据结构的特征。而 python 的字典的字符串表现形式的规则看上去和 json 类似,但是字典本身是一个完整的数据结构,实现了一切自身该有的算法
Python的字典key可以是任意可hash对象,json只能是字符串
形式上有些相像,但JSON是纯文本的,无法直接操作
python dict 字符串用单引号,json强制规定双引号
python dict 里可以嵌套tuple,json里只有array
json key必须是字符串, python 是hashable
6.生成JSON
import json
data = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
json = json.dumps(data)
print(json)
运行结果:
{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
7.将json换行为Python的dict
import json
jsonData = '{"a": 1,"b": 2,"c": 3,"d": 4,"e": 5}'
text = json.loads(jsonData)
print(type(text))
print(text)
运行结果:
<class 'dict'>
{'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
验证码
1.生成图片验证码
PIL是Python的一个第三方图片处理模块
我们也可以使用它来生成图片验证码 PIL安装
在终端中输入如下安装命令:(也可以在虚拟环境中执行)
pip3 install pillow
总结
Python可以很方便的生成图片
至于图片上放什么样的信息,可以根据需要自行斟酌
当成一张图片后,可以将这个图片进行存储
存储之后至于怎样使用这张图片就根据需要进行选择
2.网页中显示验证码-升级版
在上一节课程中说到:验证码都是以文件的方式保存
如果要在web中使用验证码,不可能每次都先生成验证码图片,先保存到磁盘,再返回给前端 web
这样会增加磁盘的开销
另外频繁产生的验证码也会占用大量的磁盘空间
这时,可以使用 BytesIO模块
使验证码图片的读写直接在内存中进行,并直接返回给前端
同时将正确验证码字符串存在session中,当用户提交表单时,就可以和session中的正确字符串作比较了
登录时添加图片验证码
为了能够让浏览器获取到图片验证码,需要一个视图函数,如下
注册
请求钩子
1.flask的4种请求钩子
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
在请求开始时,建立数据库连接;
在请求开始时,根据需求进行权限校验;
在请求结束时,指定数据的交互格式;
为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:
1)
before_first_request
在处理第一个请求前执行
2)
before_request
在每次请求前执行
如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
3)
after_request
如果没有抛出错误,在每次请求后执行参数:视图函数作出的响应
在此函数中可以对响应值在返回之前做最后一步修改处理
需要将参数中的响应在此参数中进行返回
4)
teardown_request
在每次请求后执行
接受一个参数:错误信息,如果有相关错误抛
2.代码测试
from flask import Flask
from flask import abort
app = Flask(__name__)
# 在第一次请求之前调用,可以在此方法内部做一些初始化操作
@app.before_first_request
def before_first_request():
print("before_first_request")
# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
print("before_request")
# if 请求不符合条件:
# return "laowang"
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(e):
print("teardown_request")
@app.route('/')
def index():
return 'index'
if __name__ == '__main__':
app.run(debug=True)
注意:
在debug模式下,teardown_request装饰的函数不会执行;
after_request请求钩子会自动传入response对象作为参数,同时必须返回一个response对象;
before_request装饰的函数不需要返回数据,如果返回了数据,那么视图函数不会再执行,而是直接返回结果。
上下文
1.上下文:相当于一个容器,保存了 Flask 程序运行过程中的一些信息
2.Flask中有两种上下文
请求上下文
应用上下文
1. 请求上下文(request context)
在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、sessi
request
封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user')获取的是get请求的参数
session
用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id可以记录用户信息。还可以通过session.get('name')获取用户信息
2 应用上下文(application context)
它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对 app 的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是{% em color="#fff700" %}伴 request 而生,随 request 而灭{% endem %}
应用上下文对象有:
current_app
g
#current_app
应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
应用的启动脚本是哪个文件,启动时指定了哪些参数
加载了哪些配置文件,导入了哪些配置
连了哪个数据库
有哪些public的工具类、常量
应用跑再哪个机器上,IP多少,内存多大
current_app.name
current_app.test_value='value'
3)g变量
g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc'
注意:不同的请求,会有不同的全局变量
4)****两者区别:
请求上下文:保存了客户端和服务器交互的数据
应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等
上下文中的对象只能在指定上下文中使用,超出范围不能使用 请求上下文和应用上下文原理实现:
模板中特有的变量和函数
你可以在自己的模板中访问一些 Flask 默认内置的函数和对象
1)g变量
在视图函数中设置g变量的 name 属性的值,然后在模板中直接可以取出
{{ g.name }}
2)session
为Flask的session对象
{{session.new}}
True
3)url_for()
url_for会根据传入的路由器函数名,返回该路由对应的URL,在模板中始终使用url_for()就可以安全的修改路由绑定的URL,则不比担心模板中渲染出错的链接:
{{url_for('home')}}
CSRF、数据库的迁移
CSRF
1.什么是CSRF
CSRF全拼为Cross Site Request Forgery,译为跨站请求伪造
CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求
包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......
成的问题:个人隐私泄露以及财产安全
#2. CSRF攻击示意图
客户端访问服务器时没有同服务器做安全验证
总结:
如果登录了某个网站,当用户登录后,本地浏览器就存储了cookie;当这个cookie值还在有效期内,用同一个浏览器访问了条鱼网站,那么此时就可能在用户不知情的情况下,对之前的网站进行了提交数据,从而可能发生财产损失。
防止CSRF攻击
1)在浏览器向服务器请求时,服务器通过session的方式将得到的随机值(可以用hash)存储在服务器
2)且在页面中添加一个隐藏(隐藏的目的是不让用户看见,看见也没有任何用处)的的值,这个值与上一步的值一样
3)在用户点击提交的时候,将隐藏的那个值一起传递给后端服务器
4)服务器接下来从session提取第1步生成的数据,与第3步隐藏的那个数据进行判断是否想相等
5))果比较之后两值一样,那么代表是正常的请求 6. 如果没取到或者比较不一样,代表不是正常的请求,不执行下一步操作
代码实现
导出requirements.txt
1.现在的flask项目是在本地电脑上进行开发的,当把项目推送到远程的服务器上之后,
需要安装本项目需要的Python相关包以及模块
怎么才能知道这个项目用到什么Python模块呢?
2.解决思路:
在本地电脑开发完成之后,通过相关命令导出这个虚拟环境中用到的模块
将这个用到的模块信息,存储到一个文件中,一般我们叫做requirements.txt
将这个requirements.txt文件与项目工程一起上传到远程服务器
在远程服务器上用相关命令直接安装requirements.txt文件中记录的模块名
导出包或者模块到requirements.txt
3.导出包或者模块到requirements.txt
查看当前虚拟环境中所有的包或者模块名字
pip3 list
将用到的包或者模块信息直接存储到文件
pip3 freeze >requirements.txt
pip3 freeze命令的作用是将本循环环境中用到的所有的包或者模块的名字显示出来包括版本,然后通过命令的重定向功能存储到requirements.txt文件中
如果以后项目用到的模块进行了修改,记着再重新生成一次这个文件
4.根据requirements.txt中信息安装依赖的包、模块
当我们在一个新的虚拟环境下进行安装项目依赖的包、模块时,仅仅需要
建立虚拟环境
执行安装命令pip3 install -r requirements.txt
5.总结
想必学习本小节的知识后,你会对虚拟换的使用有了一个重新的认识,如果之前没有简历虚拟环境,此时肯定就会遇到些麻烦,因为没有虚拟环境也就意味着用的是系统的开发环境,保不准里面有很多模块不是flask需要的,如果此时导出的话,就会多出来了很多不需要的模块信息。可见虚拟环境的使用还是有必要的
导出pip3 freexe > requirements.txt
安装pip3 install -r requirements.txt
数据库迁移
1.开发程序的过程中,会发现有时需要修改数据库模型,而且修改之后还需要更新数据库。
之前我们使用SQLAlchemy,它能够删除数据库或者创建数据库。也就是说之前在开发过程中,如果遇到修改数据库的时候,我们的做法有2种:
先删除旧表然后重新生成。不过这样做会丢失数据库中的所有数据
手动操作数据库(例如使用SQL修改数据库),然后再修改模型类。这样较为麻烦,容易出错
更新数据库的设计更好方法是使用数据库迁移框架
源码版本控制工具(GIT后面讲解)可以跟踪源码文件的变化,类似地,数据库迁移框架能跟踪数据库模型的变化,然后增量式的把变化应用到数据库中。
SQLAlchemy 的主力开发人员编写了一个迁移框架,称为Alembic。除了直接使用Alembic 之外,Flask 程序还可使用Flask-Migrate(这个扩展对Alembic 做了轻量级包装,并集成到Flask-Script 中,所有操作都通过Flask-Script 命令完成
2.安装
pip3 install flask-migrate
3.使用
这个扩展的初始化方法如下所示:
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
# manager.run()
为了导出数据库迁移命令,Flask-Migrate 提供了一个MigrateCommand 类,可附加到Flask-Script 的manager 对象上。在这个例子中,MigrateCommand 类使用db 命令附加。
1)3.1 创建仓库
在维护数据库迁移之前,要使用init 子命令创建迁移仓库:
python3 main.py db init
执行命令前
执行命令后
这个命令会创建migrations 文件夹,所有迁移脚本都存放其中。
一般我们会把数据库迁移仓库中的文件要和程序的其他文件一起纳入版本控制
2) 创建迁移脚本
数据库迁移用迁移脚本中有两个函数,分别是:
upgrade()函数把迁移中的改动应用到数据库中
downgrade()函数则将改动删除
Alembic 具有添加和删除改动的能力,因此数据库可重设到修改历史的任意一点。
migrate 子命令用来自动创建迁移脚本:
python3 main.py db migrate -m "initial migration"
3)更新数据库
检查并修正好迁移脚本之后,我们可以使用db upgrade 命令把迁移应用到数据库中:
python3 main.py db upgrade
执行downgrade 删除刚才的操作
python3 main.py db downgrade
例:
请将上述的最新版数据库,降级到最初的样子,然后使用upgrade可以指定升级到哪个版本
python3 main.py db upgrade 版本号