flask介绍

发布时间 2023-08-22 19:54:14作者: 星空看海

一 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})  # 字典格式

配置中心

集群化部署