Day1
flask依赖包
当执行pip install flask后,会发现多了这些依赖
1.Flask flask核心库
2.Jinja2 模板渲染库
3.Markupsafe 安全标签库 只要flask返回模板或者标签时,都会依赖它
4.Werkzeug 德文“工具”的意思,应用程序网关接口库 WSGI,flask项目启动基于Werkzeug
启动项目实现hello world
注意:尽量不要使用pycharm中自带的flask模板创建flask
启动服务:
from flask import Flask # 导入flask核心类,创建Flask应用对象
app = Flask(__name__) # app-application,实例化的flask对象
app.run() # 启动Flask
实现helloworld
from flask import Flask
app = Flask(__name__)
@app.route("/index"): # 为flask应用对象增加路由
def index(): # 与路由绑定的视图函数
return "hello world" # 返回,相当于响应
if __name__ == '__main__':
app.run()
response
1.return "hello world"
2.return render_template("xxx.html")
html文件的默认存放路径是/templates
下
3.return redirect("/home")
重定向路由
@app.route("/home")
def home():
return "home主页"
@app.route("/red")
def red():
return redirect("/home")
当访问/red
时,http status 302,3xx表示当前浏览器要做出指令。
请求时,response header中有参数location:http://127.0.0.1:5000/home,就会根据location对应的地址跳转。
小知识:pycharm中redirect("/home")
的("/home")
会有小黄块,提示的意思是,不知道这个/home存在或者不存在,所以可以把templates标记为templates folder,这样黄色的小块就不在了。
4.return send_file("文件路径")
打开,并返回文件内容,并自动识别文件类型,在response header中加入,不能识别的类型,仅会做下载处理。(其实所有的都会下载,只是浏览器能够识别图片、map4,所以给你播放出来了。并且把Content-Type作为标签放到了页面上,识别文件类型的能力取决于浏览器,如wma文件,微软自带的录音文件格式,所以只有ie浏览器能识别,而其他浏览器不能识别)
如:
放一个jpg文件,response header中Content-Type: image/jpeg
放一个mp4文件,response header中Content-Type: video/mp4
Content-Type :文件类型
二进制文件的第一行,会写文件类型,如果没有写文件类型,就是普通的文本文件。
5.jsonify 返回标准格式
@app.route("/get_json")
def get_json():
data = {"username": "tony"}
return jsonify(data)
Content-Type: application/json
flask1.1.1以上版本,可以直接使用 return data,即return一个字典时,就是在执行jsonify,不建议使用,因为要兼容低版本。
修改app.config['JSONIFY_MINETYPE'],就是修改jsonify解析时返回的response header中Content-Type
request
1.请求方式
@app.route("login",method=["GET","POST"]) #在路由后面添加路由支持的请求方式
2.获取请求参数
@app.route("/login", methods=["GET", "POST"])
def login():
print(request.form)
print(request.url)
print(request.url_charset)
print(request.url_root)
print(request.url_rule)
print(request.values)
print(request.values.to_dict())
print(request.args.to_dict())
data = {"username": "tony"}
return jsonify(data)
request.form
,获取formdata中的数据
to_dict()
,如果类型是ImmutableMultiDict,都可以用to_dict()
request.url
,获取请求的完整url
request.url_charset
,获取请求url的编码方式
request.url_root
,获取请求url的ip和port
request.url_rule
,获取请求路由
request.values
,获取post、get请求的请求数据,包括url和formdata中的数据
request.args
,获取请求参数,类型是ImmutableMultiDict,支持to_dict(),get(key)
3.上传文件、保存文件
html文件部分
<body>
<form action="http://127.0.0.1:5000/upload" method="post" enctype="multipart/form-data">
上传文件:<input type="file" name="my_file">
<button type="submit">提交</button><br>
</form>
后端:
@app.route("/upload", methods=["POST"])
def upload():
print(request.files) # FileStorage对象是Flask的文件特殊对象
# 打印结果:ImmutableMultiDict([('my_file', < FileStorage: '' ('application/octet-stream') >)])
my_file = request.files.get("my_file") # 获取FileStorage文件对象
my_file.save(my_file.filename) # 保存文件
return "200 ok"
4.其他数据
request.headers
request.cookies
request.path==request.url_rule
request.host
request.host_url
5.特殊的提交方式获取
content-type:application/json
这时,request.form、request.arg是没有数据,放在request.json中
request.json:获取content-type:application/json 时提交的数据
content-type无法被识别时,放在request.data中
request.data:获取content-type无法被识别或不包括有form字眼,原始请求中的数据 b''
6.登录示例:
前端:
<body>
<form action="http://127.0.0.1:5000/login" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="pwd"><br>
<button type="submit">提交</button><br>
</form>
flask
@app.route("/index")
def index():
return "欢迎进入首页"
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
return render_template("login.html")
if request.method == "POST":
if request.form.get("username") == "ltx" and request.form.get("pwd") == "123":
return redirect("/index")
else:
return "error,用户名或密码错误"
Flask session
Session 服务器端键值对
Cookie 客户端的键值对
flask的session交由客户端保管机制:
1.开启session[username]=123
{
username:123
}
2.序列化字典==字符串
3.加密字符串SecretKey 密钥字符串
接收反序列化session
1.从cookie中获取到一个叫session key的值
eyJ1c2VyIjoibHR4In0.Y2vCsg.VQloai4PfMQHN_gOp_bLUvdaEkQ
2.通过SecretKey 解密session
3.反序列化成字典
@app.route("/index")
def index():
if session.get("user")=="ltx": #判断session的用户名是否正确
print(session.get("user"))
return "欢迎进入首页"
else:
return redirect("/login")
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
return render_template("login.html")
if request.method == "POST":
if request.form.get("username") == "ltx" and request.form.get("pwd") == "123":
session["user"]=request.form.get("username") #新增session
return redirect("/index")
else:
return "error,用户名或密码错误"
Jinja2
{{}} 引用变量参数
{%%} 逻辑代码
jinja2的简单实用:变量、循环、if判断
# 模拟数据库
STUDENT = {'name': 'Old', 'age': 38, 'gender': '中'}
STUDENT_LIST = [
{'name': 'Old', 'age': 38, 'gender': '中'},
{'name': 'Tony', 'age': 31, 'gender': '男'},
{'name': 'EDU', 'age': 39, 'gender': '女'}
]
STUDENT_DICT = {
1: {'name': 'Old', 'age': 38, 'gender': '中'},
2: {'name': 'Tony', 'age': 31, 'gender': '男'},
3: {'name': 'EDU', 'age': 39, 'gender': '女'}
}
stuinfo.html
<body>
{{stu_info}}
<table border="1">
<tr>
<td>name</td>
<td>age</td>
<td>gender</td>
</tr>
<tr>
<td>{{stu_info.name}}</td>
<td>{{stu_info.get("age")}}</td>
<td>{{stu_info["gender"]}}</td>
</tr>
</table>
{{stu_list}}
<table border="1">
<tr>
<td>name</td>
<td>age</td>
<td>gender</td>
</tr>
{% for stu in stu_list %} <!--for循环-->
<tr>
<td>{{stu.name}}</td>
<td>{{stu.get("age")}}</td>
<td> <!--if判断-->
{% if stu["gender"]!='男' and stu["gender"]!='女'%}
酷儿
{% else %}
{{stu["gender"]}}
{% endif %} <!--if结束-->
</td>
</tr>
{% endfor %} <!--for循环结束-->
</table>
{{stu_dict}}
<table border="1">
<tr>
<td>id</td>
<td>name</td>
<td>age</td>
<td>gender</td>
</tr>
{% for foo,item in stu_dict.items() %}
<tr>
<td>{{foo}}</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
<td>{{item.gender}}</td>
</tr>
{% endfor %}
</table>
</body>
flask
# 1.查看STUDENT放在studentinfo页面
@app.route("/stu")
def stu():
return render_template("stuinfo.html", stu_info=STUDENT,stu_list=STUDENT_LIST,stu_dict=STUDENT_DICT)
可以自己创建一个标签,传入参数:
image.png
Day2
Flask路由参数
1.endpoint
不能重复,对应视图函数 ,endpoint默认是视图函数名。
请求时,通过路由找到endpoint,再通过endpoint找到对应的视图函数。
是否能反向操作,能通过endpoint找回路由吗?
通过url_for
反向创建路由。
@app.route("/look_test", endpoint="/look")
def look():
print(url_for("/look")) # 通过endpoint找到路由,所以结果是:/look_test
return "999"
2.methods
支持的请求方式,列表形式:methods=["GET","POST"]
3.redirect_to
永久重定向,308,没有进入到视图层面直接跳转。
正常情况下:
请求-->路由匹配-->通过路由找到endpoint-->通过endpoint找到视图函数
redirect_to:
请求-->路由匹配时,发现有redirect_to,立即返回,不用再走后面的部分。
应用场景:当前地址不存在时,直接给你跳转到redirect_to的地址。api发生变更了,老用户没有新的访问地址,可以使用redirect_to。
4.strict_slashes
对url最后的/是否严格。
strict_slashes=True
严格:url最后的有/不能访问,404
strict_slashes=False
不严格:url最后的有/也可以访问
5.default
默认参数,如果有默认参数,视图函数中一定要接收参数,否则就会异常。
6.动态参数
<类型:变量>
类型:int string
@app.route("/look/<int:page>",methods=["GET"])
def look(page):
print(page)
return "这是第{}页".format(page)
访问:http://127.0.0.1:5000/look/10
@app.route("/look/<folder>/<filename>", methods=["GET"])
def look(folder, filename):
filepath = os.path.join(folder, filename)
return send_file(filepath)
传入不带类型的,不写默认为string
@app.route("/look/<folder>/<filename>", methods=["GET"])
def look(folder, filename):
filepath = os.path.join(folder, filename)
return send_file(filepath)
Flask 初始化(实例化)配置
app = Flask(__name__)
从Flask跳转进去,init参数如下:
1.template_folder
template模板路径,默认为
templates
app = Flask(__name__,template_folder="templates_new")
2.static_folder
静态文件路径,默认为static
,static_folder的实现方式就是send_file。
在根目录下新建static
和image
文件夹,分别方上1.jpg和2.jpg
访问:http://127.0.0.1:5000/image/2.jpg
,报错404,
访问:http://127.0.0.1:5000/static/1.jpg
,展示图片,跟send_file效果一样。
修改static_folder路径app = Flask(__name__,static_folder="image")
,访问:http://127.0.0.1:5000/image/2.jpg
,展示图片,而访问:http://127.0.0.1:5000/static/1.jpg
报错404。
3.static_url_path
静态文件访问路径,默认值是static_folder
4.static_host
静态文件访问服务,比如:
<img src="/static/2.jpg" alt="">
也能访问到图片,因为自动拼接出host。
Flask config配置
1.app.default_config
默认配置
default_config = ImmutableDict(
{
"ENV": None,
"DEBUG": None,
"TESTING": False,
"PROPAGATE_EXCEPTIONS": None,
"SECRET_KEY": None,
"PERMANENT_SESSION_LIFETIME": timedelta(days=31),
"USE_X_SENDFILE": False,
"SERVER_NAME": None,
"APPLICATION_ROOT": "/",
"SESSION_COOKIE_NAME": "session",
"SESSION_COOKIE_DOMAIN": None,
"SESSION_COOKIE_PATH": None,
"SESSION_COOKIE_HTTPONLY": True,
"SESSION_COOKIE_SECURE": False,
"SESSION_COOKIE_SAMESITE": None,
"SESSION_REFRESH_EACH_REQUEST": True,
"MAX_CONTENT_LENGTH": None,
"SEND_FILE_MAX_AGE_DEFAULT": None,
"TRAP_BAD_REQUEST_ERRORS": None,
"TRAP_HTTP_EXCEPTIONS": False,
"EXPLAIN_TEMPLATE_LOADING": False,
"PREFERRED_URL_SCHEME": "http",
"JSON_AS_ASCII": None,
"JSON_SORT_KEYS": None,
"JSONIFY_PRETTYPRINT_REGULAR": None,
"JSONIFY_MIMETYPE": None,
"TEMPLATES_AUTO_RELOAD": None,
"MAX_COOKIE_SIZE": 4093,
}
)
DEBUG 开启代码编辑时重启、Log打印级别最低、错误信息透传
TESTING 无限接近生产、不会开启代码编辑重启、Log打印级别较高、错误信息不透传
SECRET_KEY 设置secret_key,生成session,前面说过
app.config["SECRET_KEY"]="@@#!#!@"
app.secret_key="@@#!#!@"
# 效果都是一样的,但app.config["SECRET_KEY"]是直接改配置,效率更高
PERMANENT_SESSION_LIFETIME session的生命周期,默认是31天,如果修改值的话,单位是秒,默认值timedelta(days=31)
代表31天对应的秒。app.config["PERMANENT_SESSION_LIFETIME"]=3600*24
SESSION_COOKIE_NAME 存放在浏览器cookie中session名,第一个字符不能是空格
JSONIFY_MIMETYPE jsonify的标准格式,前面有说过
2.Flask 快速配置
使用app.config.from_object()快速切换不同环境配置
在settting.py存放各项配置
class DebugConfig: # 开发环境
DEBUG = True
SECRET_KEY = "123!@#$%%^"
class TestingConfig: # 测试环境
DEBUG = False
SECRET_KEY = "正式环境需要一个很复杂的SECRET_KEY"
导入 from setting import DebugConfig,TestingConfig
,使用app.config.from_object(TestingConfig)
或app.config.from_object(DebugConfig)就能快速切换不同环境的配置
from flask import Flask
from setting import DebugConfig,TestingConfig
app = Flask(__name__)
app.config.from_object(TestingConfig)
@app.route("/index")
def index():
return "欢迎你呀"
if __name__ == '__main__':
app.run()
BluePrint 蓝图
蓝图的作用是 :功能隔离、路由隔离
1.蓝图示例一:
根目录下,有程序的入口:s2-bp.py
,其他的模块app_01
。
s2-bp.py
from flask import Flask
app = Flask(__name__)
# 建立蓝图注册
from app_01.views import user
app.register_blueprint(user)
if __name__ == '__main__':
app.run()
在app_01下views.py文件
from flask import Blueprint
# BluePrint 当作一个不能run的Flask实例
user = Blueprint("user", __name__) # 参数:先是蓝图的名字
@user.route("/login")
def login():
return "BP login"
此时访问:127.0.0.1:5000/login ,返回BP login
2.蓝图示例二
多个模块有同个路由,比如一个模块app_01
是管理后台的登录接口,一个模块app_02
有C端用户登录接口。使用url_prefix
url前缀参数解决。
s2-bp.py
from flask import Flask
app = Flask(__name__)
# 蓝图 建立蓝图注册
from app_01.views import user
from app_02.views import app_user
app.register_blueprint(user)
app.register_blueprint(app_user)
if __name__ == '__main__':
app.run()
app_01下的views.py
from flask import Blueprint
user = Blueprint("user", __name__,url_prefix="/admin") # 添加前缀
@user.route("/login")
def login():
return "admin 管理端 BP login"
app_02下的views.py
from flask import Blueprint
app_user = Blueprint("app_user", __name__,url_prefix="/shop")
@app_user.route("/login")
def login():
return "shop商城 BP login"
在访问路由时,加上对应的前缀,如:
访问http://127.0.0.1:5000/shop/login
,是访问app_02中的login
访问http://127.0.0.1:5000/admin/login
是访问app_01中的login
Flask 特殊装饰器
@app.before_request
@app.after_request
@app.errorhandler(4xx or 5xx)
1.@app.before_request
请求进入视图函数之前
2.@app.after_request
请求结束,返回响应客户端之前
定义函数的顺序:be1--be2--be3--af1--af2--af3
正常 : be1--be2--be3--vf--af3--af2--af1
异常: be1--be2--阻断--af3--af2--af1
3.@app.errorhandler(4xx or 5xx) 自定义错误处理
@app.errorhandler(404)
def error404(ErrorMassage):
print(ErrorMassage)
return "页面{}不存在".format(request.path)
Flask endpoint补充
没懂 有机会再学
Day3
Flask 蓝图结构
1.回顾蓝图
前面翻翻吧
2.一个简单的蓝图结构
根目录下新建一个app01文件夹:
-static
-templates
-views
-models.py
根目录下新建一个manage.py文件
在app01下的__init__.py
文件中:
from flask import Flask
from app01.views.user import user_bp
def create_app():
app = Flask(__name__)
# 配置
app.config["DEBUG"]=True
# 注册蓝图
app.register_blueprint(user_bp)
return app
views下的user.py
from flask import Blueprint
user_bp = Blueprint("user", __name__)
@user_bp.route("/login")
def login():
return "登陆成功"
根目录下manage.py
from app01 import create_app
app=create_app()
if __name__ == '__main__':
app.run()
在__init__.py
文件创建Flask实例、配置config、注册蓝图
在manage.py调用app.run()方法
views.py 注册路由、视图函数
使用python manage,py
启动项目。
Flask CBV
flask中有两种视图,一种是基于函数的视图FBV,一种是基于类的视图CBV
flask视图函数注册路由的本质:
其实,route装饰器内部也是通过调用add_url_rule()方法实现的路由注册,只是route装饰器看起来更加美观。
from flask import Flask, views # 导入CBV的视图基类
app = Flask(__name__)
class Login(views.MethodView): # 继承CBV视图基类的最高类
def get(self): # get方法的视图函数实现
return "GET OK 200"
def post(self):
return "POST OK 200"
# 为CBV添加路由
app.add_url_rule("/login", view_func=Login.as_view(name="login"))
if __name__ == '__main__':
app.run()
as_view
把视图类转化为视图函数
Flask 监听端口 数据传递
app.run("127.0.0.1",5000)
IP对应本机访问IP,如果是0.0.0.0表示本机所有IP
端口对应应用程序
应用层:
浏览器-端口9528,对应用192.168.14.26:9527
发起请求时,传递了b'' HTTP 1.1 / GET\t\n
网卡b'' HTTP 1.1 / GET\t\n
传递给操作系统
操作系统 解包 根据端口9527--传递给9527
WSGI:9527收到b'' HTTP 1.1 / GET\t\n
,Flask让WSGI把这些数据转换成对象environ
Flask收到WSGI转换的environ对象request_class(request类),转换为Flask的request样式,比如请求参数,转换为request.form
Flask-session
第三方组件
flask-session是flask框架的session组件,由于原来flask内置session使用签名cookie保存(交给客户端保管不安全),该组件则将支持session保存到多个地方,如:
- redis
- memcached
- filesystem
- mongodb
- sqlalchmey
安装pip install Flask-session
def create_app():
app = Flask(__name__)
# 配置
app.config["DEBUG"] = True
# 在config配置之后,在注册蓝图之前,是三方组件的空间
# 实例化一个session
Session(app)
# 注册蓝图
app.register_blueprint(user_bp)
return app
SESSION_SQLALCHEMY
数据库ORM
# session存储在redis
app.config["SESSION_TYPE"]='redis'
# 实例化
Session(app)
# 在login方法中设置session
class Login(views.MethodView): # 继承CBV视图基类的最高类
def get(self): # get方法的视图函数实现
session["username"]="电地大"
return "GET OK 200"
Redis模块的简单用法
redis安装
https://redis.io/download/
启动服务
from redis import Redis
rdb=Redis()
rdb.set("username","NB")
print(rdb.get("username"))
存一个字典
rdb=Redis()
d={"key":"value"}
# 把dict转为byte
rdb.set("username",json.dumps(d))
# 把byte转为dict
print(json.loads(rdb.get("username")))
db参数:
rdb=Redis(host="127.0.0.1",port=6379,db=8) # db-->0-15,除了0,其他都会显示,数据隔离的作用
在redis服务中命令select 8
Flask-session高级源码阅读
看不懂 就这样吧。
app.config["SESSION_TYPE"] = 'redis'
app.config["SESSION_REDIS"] = Redis(host="192.168.14.25") # 实例化,部署在内网的其他服务器
Session(app)
Day4
Flask请求上下文预备知识
1.线程安全
2.偏函数
3.OOP 面向对象
面向对象回顾
偏函数
简单的理解偏函数,它是对原始函数的二次封装,是将现有函数的部分参数预先绑定为指定值,从而得到一个新的函数,该函数就称为偏函数。相比原函数,偏函数具有较少的可变参数,从而降低了函数调用的难度。
from functools import partial
def abfun(a, b):
print(a)
print(b)
return a + b
new_ab = partial(abfun, 2, 3)
print(new_ab) # functools.partial(<function abfun at 0x000001BA7781D0D8>, 2, 3)
print(new_ab()) # 5
将原函数和原函数接收的参数一并存放,返回新函数,在执行新函数时,将参数传入原函数中一并执行。
from functools import partial
def abfun(a, b,x):
print(a)
print(b)
return a + b+x
# 只知道一个参数的值
new_ab = partial(abfun,x=80)
print(new_ab)
print(new_ab(3,10)) # 93
线程安全
单线程读取19个,需要19s,打印内容为012345...19
import time
class Foo:
pass
f = Foo()
f.num = 0
def add(i):
f.num = i
# 模拟IO操作需要1s
time.sleep(1)
print(f.num)
for i in range(20):
add(i)
开启多线程,使任务在1.5s左右完成:
但打印的结果是19 19 19...19
import time
from threading import Thread
class Foo:
pass
f = Foo()
f.num = 0
def add(i):
f.num = i
time.sleep(1)
print(f.num)
for i in range(20):
task=Thread(target=add,args=(i,))
task.start()
使用深copy:
很快解决了阻塞 保证了公共对象的安全性
但浪费了大量的资源 拿空间浪费换取时间
import time
from threading import Thread,get_ident
from copy import deepcopy
class Foo:
pass
f = Foo()
f.num = 0
local_dict={}
def add(i):
# 深copy
local_dict[get_ident()]=deepcopy(f)
local_dict[get_ident()].num = i
time.sleep(1)
print("{}_{}".format(local_dict[get_ident()].num,get_ident()))
# print(get_ident())
for i in range(20):
task=Thread(target=add,args=(i,))
task.start()
WSGI收取request
请求上下文:读flask源码
1.请求是如何到达flask应用的
由底层传到高层
请求上文
from werkzeug.wrappers import Request,Response
from werkzeug import run_simple
# run_simple监听操作系统传递来的数据
@Request.application
def app(req):
print(req,req.method,type(req))
return Response("200 ok")
run_simple("127.0.0.1",5000,app)
打印结果:
req # <Request 'http://127.0.0.1:5000/' [GET]>
req.method # GET
type(req) #<class 'werkzeug.wrappers.request.Request'>
falsk中的app.run()
调用的就是run_simple()
方法
一顿转换,转换为flask风格的request
environ是原始的请求信息,app
golbals.py中 --》请求上文
image.png
网友评论