flask介绍
目录
一 web框架介绍
# python 界的web框架
-Django:大而全,快速开发,公司内部项目使用的多
-Flask:小而精,不具备web开发好多功能,丰富的第三方插件
-FastApi:异步框架,主要为了做前后端分离接口
-Sanic:异步框架,只支持python3.6 及以上,性能比较高
-Tornado:公司用的比较少...
1.2 fastapi
FastApi官网:https://fastapi.tiangolo.com/zh/
-FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Python 3.6+ 并基于标准的 Python 类型提示。
-可与 NodeJS 和 Go 并肩的极高性能(归功于 Starlette 和 Pydantic)。最快的 Python web 框架之一。
# pip install fastapi
# pip install uvicorn
import time
from fastapi import FastAPI
app = FastAPI()
@app.get('/') # 向根路径发送请求
async def index():
return {'code': 100, 'msg': '成功'}
# 运行服务器命令:uvicorn py文件名:app --reload
# 启动后,我们修改代码会自动重启,监听8000端口
import time
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def index(): # 加上async,就是协程函数
await time.sleep(3)
return {'code': 100, 'msg': '成功'}
@app.get('/home')
async def home():
time.sleep(2)
return {'code': 100, 'msg': 'home'}
@app.get('/order')
async def order():
time.sleep(2)
return {'code': 100, 'msg': 'order'}
# 如果是django,flask,这种同步框架,他会开启三个线程来处理这三个请求
# fastapi,sanic这种框架,就只用一个线程处理这三个请求(单线程下的并发),开启第一个遇到IO操作,就会切换,去运行第二个函数。
二 flask
# Flask 框架
-pip3.8 install flask
2.1 第一个flask项目
from flask import Flask
app = Flask(__name__) # 传入import_name字符串
@app.route('/', methods=['GET'])
def index():
return 'index字符串'
if __name__ == '__main__':
app.run() # 运行flask
# 指定端口
# app.run(port=8000)
2.2 pycharm创建flask框架(可以不用)
三 wsgiref
# 服务 wsgi协议的web服务器,django的web服务用的就是它
# 相当于个socket服务端,可以接收客户端发送过来的请求,处理,返回给客户端
from wsgiref.simple_server import make_server
def mya(environ, start_response): # django自己写了个类,在里面自己写逻辑
print(environ)
# django在这个位置是中间件
start_response('200 OK', [('Content-Type', 'text/html')])
# 这里是路由分发
if environ.get('PATH_INFO') == '/index':# 根据不同路由,匹配不同视图函数
with open('index.html','rb') as f:
data = f.read()
elif environ.get('PATH_INFO') == '/login':
with open('login.html', 'rb') as f:
data = f.read()
else:
data = b'<h1>Hello, web!</h1>'
return [data]
# 底层是socket服务端
if __name__ == '__main__':
myserver = make_server('', 8011, mya) # 监听本地的8011端口,当请求来了,就会执行 mya(),传入两个参数,一个是environ:http请求转成python的字典,一个是start_response:响应对象
print('监听8010')
myserver.serve_forever()
# Django和flask就是mya这个可调用对象,只是上面的代码写成个函数的形式
# environ 环绕;包围;围住
# Django的原理
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'beluga_api.settings.prod')
application = get_wsgi_application()
# 请求来了,就会执行application(environ, start_response)
# 是WSGIHandler()类的对象,对象加括号,触发类的__call__(environ, start_response)的执行
# 源码:
def get_wsgi_application():
django.setup(set_prefix=False)
return WSGIHandler()
class WSGIHandler(base.BaseHandler):
def __call__(self, environ, start_response):
return response # 返回response对象
# flask原理
from flask import Flask
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return 'index字符串'
if __name__ == '__main__':
app.run()
# 请求来了,会执行 app(environ, start_response)--->
# Flask类的---__call__(environ, start_response)
# 源码
class Flask(Scaffold):
def __call__(self, environ: dict, start_response: t.Callable) -> t.Any:
return self.wsgi_app(environ, start_response)
四 Werkzeug
Werkzeug是一个WSGI工具包(在它基础上,继续封装),他可以作为一个Web框架的底层库。这里稍微说一下, werkzeug 不是一个web服务器,也不是一个web框架,而是一个工具包,官方的介绍说是一个 WSGI 工具包,它可以作为一个 Web 框架的底层库,因为它封装好了很多 Web 框架的东西,例如 Request,Response 等等
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
# run_simple('ip地址', port, 可调用对象)
run_simple('localhost', 4000, hello)
# flask基于 Werkzeug封装的,更简单一些
# django是基于 wsgi封装的
五 Flask快速使用
5.1 登录显示用户信息的小案例
from flask import Flask, render_template, request, redirect, session
# template_folder页面文件夹
app = Flask(__name__, template_folder='templates')
# 配置文件的配置
app.debug = True # 设置后可以自动重启
app.secret_key = 'sdfsdfsdfsdf' # 等同于django的 配置文件有个很长的秘钥
USERS = {
1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}
@app.route('/login', methods=['GET', 'POST'])
def login():
# 只要在这个函数中,全局的request 就是当次请求的request对象,等同于django的request
if request.method == 'GET': # get请求返回模板
return render_template('login.html') # 新手四件套之一,返回模板
else:
username = request.form.get('username') # 取值
password = request.form.get('password')
if username == 'lqz' and password == '123':
# 登录成功,把登录信息,写入到session中
session['username'] = username
# return redirect('http://www.baidu.com') #重定向到百度,新手四件套之一,返回重定向
return redirect('/index') # 重定向到我的地址,新手四件套之一,返回重定向
else:
return render_template('login.html', error='用户名或密码错误')
@app.route('/index') # methods不写,默认是GET请求
def index():
# 判断它是否登录,如果登录,显示用户信息
if session.get('username'):
return render_template('index.html',users=USERS)
else:
return redirect('/login')
# Django的有名或者无名分组、转换器
@app.route('/detail/<int:pk>') # flask只有转换器
def detail(pk):
if session.get('username'):
user=USERS.get(pk) # 拿取到单个user
return render_template('detail.html',user=user)
else:
return redirect('/login')
if __name__ == '__main__':
app.run()
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="" method="post">
<p>用户名:<input type="text" name="username"></p>
<p>密码:<input type="password" name="password"></p>
<p><input type="submit" value="提交">{{error}}</p><!-- 模板语法的插值语法 -->
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
<!--Django不能加括号,也只能使用点语法取值-->
{% for k,v in users.items() %}
<tr>
<!--Jinja2可以使用函数加括号的返回值,可以使用点语法、中括号、get取值-->
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="/detail/{{k}}">查看详细</a></td>
</tr>
{% endfor %}
</table>
</body>
</html>
detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>详细信息 {{user.name}}</h1>
<div>
{{user.text}}
</div>
</body>
</html>
总结
1 新手三件套: 1) 直接返回字符串 2) 模板页面render_template 3) 重定向redirect
2 flask的路由写法,是基于装饰器的 @app.route('/detail/<int:pk>' , methods=['GET'])
3 路由转换器跟django一样
4 取出前端post请求提交的数据:request.form
5 取出请求方式:request.method
6 使用session设置值和取值
-session[key]=value
-session.get('key')
7 flask的模板语法完全兼容dtl,并且它更强大,可以加括号执行
5.2 登录认证加装饰器
# 1 装饰器使用位置,顺序
# 2 flask路由下加装饰器,一定要加endpoint,每个的视图函数的值都不一样
-如果不指定endpoint,反向解析的名字都是函数名,不加装饰器没有问题,就是正常函数index,detail
-如果加了装饰器--->index,detail都变成了inner--->反向解析的名字都是函数名inner,就会报错
# 3 使用auth这个装饰器,是把index当成参数传入,把auth的执行结果返回后,赋值给index
@auth
def index():
# 本质:index=auth(index)
执行顺序是由外到内的
先执行路由匹配,匹配成功后,再执行判断是否登录,再执行本身的视图函数
# 装饰器
def auth(func):
def inner(*args, **kwargs):
if session.get('username'):
res = func(*args, **kwargs) # 真正的执行视图函数,执行视图函数之前,判断是否登录
return res
else:
# 重定向到login页面
return redirect('/login')
return inner
@app.route('/login', methods=['GET', 'POST'],endpoint='login')
def login():
if request.method == 'GET':
return render_template('login.html')
else:
username = request.form.get('username')
password = request.form.get('password')
if username == 'lqz' and password == '123':
session['username'] = username
return redirect('/index')
else:
return render_template('login.html', error='用户名或密码错误')
@app.route('/index', endpoint='index')
@auth # 路由匹配成功,再执行校验是否登录
def index():
return render_template('index.html',users=USERS)
@app.route('/detail/<int:pk>',endpoint='detail')
@auth
def detail(pk):
user=USERS.get(pk)
return render_template('detail.html',user=user)
if __name__ == '__main__':
app.run()
语法糖
@符号后面放什么都可以,不一定非要是装饰器
# 拓展1:语法糖后放个函数
def add(func):
print(func)
@add # test=add(test) ---> test变成了None
def test():
print("test")
print(test) # None
# 拓展2:@后放个对象
class Person:
def __call__(self, func):
print(func)
return 123
p = Person()
@p # test=p(test) # p() 会触发__call__
def test():
print("test")
print(test) # 123
# __call__写成装饰器
class Person:
def __call__(self, func):
def inner(*args, **kwargs):
res = func(*args, **kwargs)
return res
return inner
p = Person()
@p # test=p(test) # p() 会触发__call__--->Person的 __call__(func)--->返回inner,以后test就是inner---> test(参数)--> 执行的是inner
def test():
print("test")
print(test) # 函数inner
# 拓展3:类装饰器:
1 装饰类的装饰器 2 类作为装饰器
# 装饰类的装饰器
def auth(func):
def inner(*args, **kwargs):
res = func(*args, **kwargs) # 此时的res就是Foo的对象f
res.name='lqz'
return res
return inner
@auth # Foo=auth(Foo)
class Foo():
pass
f=Foo() # Foo() 实际调用 --->inner(Foo)--->类实例化得到对象,返回,以后f就是Foo的对象,但是可以里面多了属性或方法
print(f)
print(f.name)
# 拓展4:有参装饰器--->额外为被装饰的函数传参数
这样Foo中也可以使用到传入进去的这个参数10
@auth(10) # Foo=auth(10)(Foo)
class Foo():
pass
六 配置文件
# django 有settings配置文件---->所有web框架都会有配置文件--->配置文件的形式可能不太一样
6.1 方式一
app = Flask(__name__)
# 配置文件方式一:只能配置debug和秘钥
app.debug = True # 设置后可以自动重启
app.secret_key = 'sdfsdfsdfsdf' # 等同于django的 配置文件有个很长的秘钥
@app.route('/index', methods=['GET'])
def index():
print(app.debug) # True
print(app.secret_key) # sdfsdfsdfsdf
return 'hello world'
6.2 方式二:app.config
# 配置文件方式二:使用app.config 等同于django中的settings的 from django.conf import settings
# 更改
app.config['DEBUG'] = True
# PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
app = Flask(__name__)
@app.route('/index', methods=['GET'])
def index():
print(app.config) # 是个字典
return 'hello world'
6.3 方式三
# 通过py文件配置,类似于django
# app.config.from_pyfile("python文件名称")
app.config.from_pyfile("settings.py")
app.config.from_pyfile("dev.py") # 类似于django的开发环境
app.config.from_pyfile("pro.py") # 类似于django的上线环境
app = Flask(__name__)
@app.route('/index', methods=['GET'])
def index():
print(app.config) # 是个字典
return 'hello world'
settings.py
DEBUG=True
SECRET_KEY="asdfasdfasdfasd333"
6.4 方式四:使用类的方式
# app.config.from_object("python类或类的路径")
app.config.from_object('settings.DevelopmentConfig') # 开发环境的配置
app.config.from_object('settings.ProductionConfig') # 上线环境的配置
app = Flask(__name__)
@app.route('/index', methods=['GET'])
def index():
print(app.config.get('DEBUG')) # True
print(app.config.get('DATABASE_URI')) # 'sqlite://:memory:'
return 'hello world'
settings.py
class Config(object): # 基类
DEBUG = False
DATABASE_URI = 'sqlite://:memory:' # 数据库配置
class ProductionConfig(Config): # 上线、生产环境
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True # 开发环境,debug 是true的,连数据库连的是sqlite
其他方式
# 通过环境变量配置的方式
# app.config.from_envvar("环境变量名称")
app.config.from_envvar(os.environ['xxx'])
# JSON方式
# app.config.from_json("json文件名称") # JSON文件名称,必须是json格式,因为内部会执行json.loads
app.config.from_json("settings.json")
# map方式
app.config.from_mapping({'DEBUG': True}) # 字典格式
配置中心
集群化部署