1.框架
Django(MVT)&Flask(轻量级,代码多,类比Java的SpringMVC) 主要实现了路由分发 和模板渲染功能
使用
pipenv
搭建虚拟环境,当然也可以使用virtualenv
,所有的包都注意版本兼容(最新)
python
from flask import Flask
# TODO:__name__ : 当前的文件所在目录就是flask项目目录,会在项目目录下寻找静态文件夹和模板文件。
# TODO : app: 贯穿整个项目,绑定路由,运行项目。
# 1: 创建视图应用 ctrl + p: 提示参数 command + p(苹果)
app = Flask(__name__)
# 2: 编写视图函数,绑定路由
#默认127.0.0.1:5000/
@app.route('/') # 参数1:路由地址信息
def hello_func():# 无需写request,导包就可以了。
# 在flask中返回的内容会被包装成响应对象
return "hello word"
# 3:运行flask:默认是5000端口
if __name__ == '__main__':
# run的参数 debug的优点: 1: 修改自动重启 2:定位bug
app.run(host='0.0.0.0', port=8000, debug=True)
# app.run()
启动
方式一run
方式二配置
export FLASK_APP = 当前路径下的文件名。
export FLSK_ENV = development
flask run -h ip地址 -p 端口号
2.路由

结果:返回一个Map类,类中是一个列表,列表分为三部分:路由,请求方式,视图。
注意:@app.route('/', methods=['get', 'POST'])
1: 如果一个视图没有写请求方式,则flask自动给get,head,options请求。
2: flask提供访问静态资源的路由信息:/static/静态文件名
3: options请求一般用于跨域访问,试探请求。
4: head请求一般用于查看服务器是否存在某种资源。
路由转化器
1:格式:<路由转换器的名称:变量名称>
2:转换器父类的所在地:from werkzeug.routing import BaseConverter
3: 在routing.py中存在一个放置路由转换器的字典。键是转换器的名称,值是转换器类名。
自定义转化器
1:自定义转换器,继承于BaseConverter
2: 重写regex属性,正则
3: 将自定义路由转换器添加到默认的转换器字典中
4: 使用自定义路由转换器完成转换。
3.请求
不是参数,分装到request( 线程隔离)
python
from flask import Flask,request
app = Flask(__name__)
@app.route('/index')
def index():
print("request.url的类型是 ", type(request.url)) #<class 'str'>
print("得到的request:", request.url) # 得到的request: http://127.0.0.1:8000/
return "get_url"
@app.route('/')
def get_remote():
print("获取访问的用户:", request.remote_user) # 获取访问的用户: None
print(type(request.remote_user)) # <class 'NoneType'>
print("获取访问的ip地址:", request.remote_addr) # 获取访问的地址: 127.0.0.1
print(type(request.remote_addr)) #<class 'str'>
return "get_remote"
@app.route('/headers')
def get_headers():
print("获取到的请求头是:", request.headers)
print("获取到的请求头类型是", type(request.headers)) # 字典对象:获取到的请求头类型是 <class 'werkzeug.datastructures.EnvironHeaders'>
print("获取请求头中的单个信息", request.headers.get('host')) # 找不到返回空
print("获取请求头中的单个信息", request.headers['host']) # 找不到报错
return "get_headers"
@app.route('/methods')
def get_methods_cookies():
# 注意获取到的是大写的,如果以后要判断方式一定要考虑大写。
print("获取到的method是: ", request.method) # 获取到的method是: GET
print(type(request.method)) # <class 'str'>
print("获取到的cookies是:", request.cookies) # 获取到的cookies是: ImmutableMultiDict([])
print(type(request.cookies)) # <class 'werkzeug.datastructures.ImmutableMultiDict'>
return "get_methods_cookies"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
方法和属性:获取请求路径,访问的用户和地址,请求头,请求方式和cookies

4.响应
python
from flask import Flask, Response, make_response, render_template, jsonify, redirect, url_for
app = Flask(__name__, # 导入名称, flask会根据该参数查询静态文件的存储路径
# 官方建议直接使用__name__, 表示从当前目录中查询静态文件存储路径
static_folder="static1", # 设置静态文件的存储目录
static_url_path='/res/img', # 设置静态文件的URL访问路径 如 127.0.0.1:5000/res/img/123.jpg
)
#如果取消转换ASCII编码:增加这个配置。
app.config['JSON_AS_ASCII'] = False
@app.route('/demo1')
def demo_01():
# 返回多个值,python会封装成元组,然后返回
return "我是响应体信息", 200, {'name':'www'}
@app.route('/demo2')
def demo_02():
# 1: 创建响应对象
response = make_response('我是响应体') # type: Response
# 2:自定义状态码
response.status_code = 201
# 201表示新建资源
# 3:自定义响应头
response.headers['name'] = 'www'
# 4: 设置cookie
# 5: 返回响应对象
return response
@app.route('/demo3')
def demo_03():
return render_template("index.html")
@app.route('/json2')
def demo_func():
# 1: 准备数据
my_dict = {
'name': 'www',
'age': 23
}
# 1: 将字典转换成json字符串
# 2: 将json字符串包装成响应对象,然后在响应头中设置了响应类型:Content-Type: application/json
json_str = jsonify(my_dict)
return json_str
@app.route('/')
def demo_redirect():
url = 'https://www.baidu.com'
return redirect(url)
@app.route('/demo')
def demo_redirect2():
# 路由反转: 根据路由名称获取url地址
url = url_for('demo_redirect')
return redirect(url)
if __name__ == '__main__':
app.run(debug=True)
访问静态资源
1:将静态资源放入到 项目的 static 文件夹中。
2:通过内置的静态资源的访问路由, URL路径格式为 /static/<filename> 案例:如 static目录放入文件 123.jpg, 则访问URL为 http://127.0.0.1:5000/static/123.jpg
3:返回模板文件作为响应
设置响应数据
- 三个返回值(元组):响应体 (字符串),状态码 (数字),响应头(字典)
- 自定义响应对象:使用make_response()函数创建响应对象,请求头无中文,态码只能是数字
返回JSON
jsonnify✔️
重定向
redirect()函数:重定向函数。
url_for()函数:路由反转函数。根据视图名,得到url路径。
5.状态保持
cookie设置响应头的set cookie字段 value必须是str/bytes类型,删除置0
session本质不是全局变量,是一个字典,服务器端,线程id隔离,设置加密密钥,有效期
JWT tocken准备载荷和密钥和过期时间(expire名字固定)

异常处理
捕获http错误
@app.errorhandler(404)
还可以捕获系统内置错误
@app.errorhandler(ZeroDivisionError)
6.高级处理
6.1请求钩子
是框架提供的生命周期拦截点,类似中间件
py
from flask import Flask, make_response, Response, request
app = Flask(__name__)
#请求钩子
# @app.before_first_request
# def inital():
# """
# 第一次请求之前调用,并且只会调用一次。
# 作用:项目的初始化操作
# :return: 不要返回值,如果存在返回值会之间返回给前端,就进入不了视图函数
# """
# print("请求之前,只调用一次")
@app.before_request
def process_request():
"""
每次请求之前调用
作用:封ip, 统一用户权限认证(cookie session jwt),用户信息的提取
:return:不需要返回值
"""
# black_list=[]
# if request.remote_addr in black_list:
# return "IP不允许访问"
print("请求之前调用,每次请求都会调用")
@app.after_request
def process_response(response):
"""
# 每一次视图执行之后调用。
作用:拦截响应对象进行统一设置。
:param response:视图函数返回的响应。
:return: 一定要返回响应,不返回前端获取不到响应。
"""
print("我在请求之后被调用了,每次请求之后都会被调用")
return response
@app.teardown_request
def process_error(error):
"""
每次请求结束的时候调用
作用:判断处理异常,收尾工作
:param error:异常处理对象
:return:
"""
print(error)
print("我会接收异常信息")
@app.route('/login')
def login():
print("我是视图函数,我被调用了")
return ""
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000, debug=True)
三个函数是装饰器
flask2.3 版本之后before_first_request被移除
装饰器两种执行方式:
方法1:语法器
@app.after_request
def func():
pass
方法2:装饰器理解成函数
@app.after_request(process_response)
appa和中间件不同时写,无@
def view_func():
pass
app.before_request(view_func)

6.2蓝图
MVT
-
代码eg. ,小心循环导包,系统同名模块
1: 在模块的初始化文件,创建蓝图对象。
2:在视图文件中,利用蓝图对象绑定路由信息。
3:将蓝图对象注册到app中。
4:在模块的初始化文件中导入视图文件中的视图。

蓝图其他参数
1:创建蓝图时, 可以通过 url_prefix参数 给蓝图定义的路由添加 统一的URL资源段前缀。
python
from flask import Blueprint
"""
1: 蓝图中可以创建属于自己的模板和静态文件
2: 创建蓝图对象的参数: 蓝图名称, 导包路径
"""
# 1: 创建蓝图对象
home_bp = Blueprint("home", import_name=__name__, url_prefix="/home")
from home.views import *
2:蓝图定义的路由, 其函数标记为 蓝图名.函数名。
python
from flask import url_for
from home import home_bp
"""
蓝图对象没有注册视图路由的能力
"""
@home_bp.route('/home')
def home_page():
url = url_for("home.home_page")
print(url)
return "home page"
3:蓝图也可以 设置请求钩子,但是只有访问该蓝图定义的路由时才会触发,实现局部监听。
python
from flask import Blueprint
"""
蓝图中创建的钩子函数,只对当前蓝图起作用,对其他蓝图不起作用
"""
# 1: 创建蓝图对象
home_bp = Blueprint("home", import_name=__name__, url_prefix="/home")
@home_bp.before_request
def home_pre_prepare():
print("home_prepare")
from home.views import *
6.3上下文
1:上下文就是一个数据容器 。
2:上下文分为请求上下文 (request和session)和应用上下文 (current_app 和 g)。
3:导包都是from flask import request, session, current_app, g。
4:本质都不是全局变量,底层通过线程进行了隔离。
5:生命周期:请求开始时创建,返回响应时销毁。
6:作用范围:默认只能视图函数内部使用,函数外部使用会报错。
py
#手动开启应用上下文
with app.app_context():
print(corrent_app)
- 当模块中够不到app时,想调用app中的配置,使用current_app获取。
- g能在钩子函数和视图函数进行参数传递。能够在视图函数和自定义函数之间进行参数传递。
每一次请求之后会清空钩子函数内部的值。 - 上下文机制不是全局变量,线程隔离
- 设置使用范围为节省内存
请求开始时,创建上下文(记录上下文变量)
请求结束时,销毁上下文(将上下文变量删除,数据占用的空间被释放
6.4综合认证
统一认证
py
from flask import Flask, session, g, abort
app = Flask(__name__)
app.secret_key = 'test'
def login_required(view_func):
def wrapper(*args,**kwargs):
#1.装饰器代码
#判断是否登录
if g.user_name and g.user_id:
return view_func(*args,**kwargs)
else:
abort(401)#权限认证失败
#2,原函数
return wrapper
#统一处理
@app.before_request
def prepare():
# 使用g变量来获取用户名
g.user_name =session.get('user_name')
g.user_id = session.get('user_id')
@app.route('/login')
def login():
session["user_name"]="www"
session["user_id"] = 23
return 'login success'
@app.route('/')
def index():
# 直接拿过来使用
if g.user_name and g.user_id:
return "欢迎:{}".format(g.user_name)
else:
return '首页'
#登录后点赞
@app.route('/like')
def like():
print("点赞成功")
return 'success'
if __name__ == '__main__':
app.run(debug=True)
需求: 获取用户身份
分析: 除了静态资源,基本所有视图都需要获取用户身份,每个视图单独获取出现大量的代码冗余
解决办法: 设置 请求钩子 ,并通过 g变量 将数据传递给视图函数
访问限制
代码demo5
先登录再...(封装成装饰器先装饰路由,再装饰登录)
装饰器主动装饰视图函数--也能防止装饰器修改被装饰函数名称和文档
6.5应用配置
python
# 方案二 创建一个配置基类
app.config.from_object(DevelopmentConfig)
# 方案三: 从环境变量中加载配置信息
app.config.from_envvar("CONFIG", silent=True)
# 方案一:
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = "python39"
app.config['TESTING'] = True
app.config['LJSON_AS_ASCII'] = False
加载配置
1: 使用app.config 配置字典 添加配置信息[少量 配置信息]
2: 使用app.config.from_object 配置类 setting.py✔️
3: 使用**app.config.from_envvar ** 环境变量
工厂方法:

7.flask-restful风格
三步操作:
-
创建扩展/组件对象:组件对象=Api(app)
-
定义类视图:class 自定义视图类(Resource)
-
组件添加类视图: 组件对象.add_resource(视图类,URL资源段)
返回字典,底层转化为json字符串

7.1视图
类视图添加装饰器
方案一:method_decorators列表的形式。
方案二:method_decorators字典的形式。
- (同中间件)装饰的顺序是:先装饰列表前面的,再装饰后面的
- 执行顺序是:先执行后面的,再执行前面的。

单例设计模式

耗时op:权限认证封装成单例
蓝图和类视图
- 创建蓝图对象 蓝图对象=Blueprint('蓝图名',_ _name __)
- 每个蓝图分别创建组件对象(1.) 组件对象=Api(蓝图对象)
- 组件对象添加类视图 (3.) 组件对象.add_resource(视图类,URL资源段)
- 应用注册蓝图 应用对象.register_blueprint(蓝图对象)
7.2请求
-
1.获取参数2.参数校验3.逻辑处理4.返回值处理
-
RequestParser 请求解析(含1.2),步骤:
- 代码
-
- 创建请求解析器 请求解析器=RequestParser()
- 添加参数规则 请求解析器.add_argument(参数名,参数规则...)
- 执行解析 参数对象=请求解析器.parse_args()
- 获取参数 参数对象.参数名
-
type的三中类型:
1: python自带的类型
2:flask_restful自己内置的
3:自己定义的函数/类。
7.3响应
序列化
- 序列化规则=(字段名:序列化类型)
- marshsl 函数 按照序列化规则 将模型对象转为字典 序列化后的字典=marshal(模型对象,序列化规则)
- 方案三:代码demo8
自定义Json
api.representation(mediatype="application/json")(output_json)
8.flask-sqlalchemy扩展
SQLAlchemy 是目前python中最强大的 ORM框架
MariaDB数据库(Mysql社区版)
8.1基本使用
- 在app对象中添加数据库连接配置信息
- 创建数据库对象-绑定应用对象
- 自动以模型类
- 根据模型类创建所有的表结构
8.2数据操作
自定义模型类(创建表),懒加载
增加数据
@app.route('/')
def index():
"""增加数据"""
# 1.创建模型对象
user1 = User(name='zs', age=20)
# = 'zs'
# user1.age = 20
# 2.将模型对象添加到会话中
db.session.add(user1)
# 添加多条记录
# db.session.add_all([user1, user2, user3])
# 3.提交会话 (会提交事务)
# sqlalchemy会自动创建隐式事务
# 事务失败会自动回滚
db.session.commit()
return "index"
查询数据

修改数据
方案一:先查询再更新
方案二:配合查询过滤器filter() 和 更新执行器update() 进行数据更新✔️
Goods.query.filter( == '方便面').update({'count': Goods.count - 1})
db.session.commit()
删除数据
方案一: 先查后删除
goods = Goods.query.filter( == '方便面').first()
# 删除数据
db.session.delete(goods)
# 提交会话 增删改都要提交会话
db.session.commit()
方案二:delete子查询
Goods.query.filter( == '方便面').delete()
# 提交会话
db.session.commit()
8.3高级机制
https://blog.51cto.com/u_15317888/4953364
刷新数据
1:Session 会先将操作产生的数据保存到内存中。
2: 在执行 flush刷新操作后, 数据操作才会同步到数据库中。
3:隐式刷新操作:1:提交会话 2:查询操作(包括更新和删除中的子查询)。
4:手动刷新:session.flush()
eg.收获地址(不注册可购买)
多表连接,flush(不提交事务)✔️&commit❌
Flush本质把ORM转化的sqI提交到数据库执行sql语句
django事务(默认关)&Flask-sqlalchemy(默认开,怎删改查都需commit)
多表查询
关系属性:oop外键
- 定义关系属性 关系属性名=db.relationship(关联数据所在的模型类")
- 外键字段设置外键参数 外健字段=db.Column(字段类型,db.ForeignKey(主表名,主键名))
- 通过关系属性获取关联数据 模型对象,关系属性
join连接查询
db.session.query(主表模型字段1,主表模型字段2,从表模型字段1,xx..).join(从表模型类, 主表模型类.主键 == 从表模型类,外键)
Session机制
- Session中可以包含多个事务,提交事务失败后,会自动执行SQL的回滚操作
- 同一个谓求中,想要在前一个事多失败的情况下创建新的事务,必须先手动回滚事务Session.rollback