flask

发布时间 2023-04-01 16:45:29作者: 李阿鸡

python web框架介绍

python主流的web框架

django 大而全,内置的app多,第三方也多

flask 小而精,没有很多的内置组件,只完成web框架最基本的功能,需要借助于第三方来完成更丰富的功能。 适合快速开发

web.py python一个小巧灵活的框架。简单且功能强大

异步的web框架

fastapi python的异步框架,很多公司都在使用.与flask的区别在于异步编写不一样用的挺多

官方网站 https://fastapi.tiangolo.com/zh/

sanic python的异步框架,支持异步高并发请求的 web 服务

tornado 异步框架。基本上不怎么用了。

同步框架与异步框架的区别

同步框架: 一个线程只处理一个请求

异步框架: 一个线程可以处理多个请求

异步框架可以提高并发量

flask介绍

flask是一个基于python开发并依赖jinja2模板和werkzeug WSGI服务的一个微型框架

jinja2 模板语法,与django的dtl非常像

werkzeug WSGI 符合wsgi协议的web服务器比django的web服务器封装了更多的东西,django使用的是wsgiref。

# 1. 使用wsgiref编写web
from wsgiref.simple_server import make_server

# mya 就等同于django只不过django在这里面把environ包装成了request
def mya(environ, start_response): 
    #environ 字典,请求里的东西
    print(environ)
    # 响应
    start_response('200 OK', [('Content-Type', 'text/html')])
    # 在request里是path environ是past_info
    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]  # django在这里做成了response

if __name__ == '__main__':
    myserver = make_server('', 8008, mya)
    print('监听8010')
    myserver.serve_forever()
# 2 使用werkzeug写web
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('localhost', 4000, hello)

快速使用

安装

pip install flask
		1.x   版本之间没有本质上的区别
		2.x	  源码上动了,用起来和原本用法一样
会自动安装依赖  MarkupSafe, Werkzeug, Jinja2, flask

快速写一个flask

from flask import Flask

# 实例化得到一个Flask对象
app = Flask(__name__)


# 注册路由,使用装饰器 login函数,访问/login会执行
@app.route('/login')
def login():
    return '啦啦啦'


@app.route('/')
def home():
    return 'home'


# 启动必须写的
if __name__ == '__main__':
    # 0.0.0.0 监听本地ip地址,局域网内都可以访问
    app.run()

# 访问http://127.0.0.1:5000/login 会返回啦啦啦

flask写登录

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="post">
    <!--写一个登录表单-->
        <input type="text" name='username' placeholder="请输入用户名">
        <!--密码表单-->
        <input type="password" name="password" placeholder="请输入密码">
        <!--写一个登录按钮-->
        <input type="submit" value="登录"> {{error}}
</form>
</body>
</html>

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>用户列表</h1>
<table>
    {%for k,v in user_dict.items()%}
    <tr>
        <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>
    <p>用户名:{{user_detail.name}}</p>
    <p>年  龄:{{user_detail['age']}}</p>
    <p>性  别:{{user_detail.get('gender')}}</p>
    <p>详  情:{{user_detail.get('text')}}</p>
</body>
</html>

功能py文件

from flask import Flask, request, render_template, redirect, session,jsonify

app = Flask(__name__)
# 想要使用session,需要设置secret_key
app.secret_key = '21312dsadg12'

# 造一个users字典
users = {
    1: {'name': 'lxj', 'age': 18, 'gender': '男', 'text': '我是lxj'},
    2: {'name': 'zyg', 'age': 68, 'gender': '不明', 'text': '我是zyg'}
}


# 需要返回一个页面,创建一个templates文件夹,里面创建一个login.html
@app.route('/login', methods=['GET', 'POST'])  # 默认只能接收get请求,如果需要接收post请求,需要指定methods=['GET','POST']
def login():
    # get请求返回模板,没有request对象,需要导入全局request对象,每个视图函数都会有一个自己的request对象
    if request.method == 'GET':
        # 新手四件套之一,返回模板reder_template ,需要导入
        return render_template('login.html')
    else:
        # post请求处理校验数据
        # 获取用户名和密码,从form里取,等同于django的request.POST
        username = request.form.get('username')
        password = request.form.get('password')
        # 校验数据
        if username == 'lxj' and password == '123':
            # 登录成功(需要保存登录状态),新手四件套之一,重定向redirect
            # 保存到session中,也需要导入全局,也是每个视图函数都会有一个自己的session,需要设置secret_key不然报错
            session['name'] = username
            return redirect('/')
        else:
            # 登录失败,返回模板,并且需要传递错误信息
            return render_template('login.html', error='用户名或密码错误')




@app.route('/')
def home():
    # 登录成功才能访问首页,不成功就重定向到登录页面
    # 从session中获取登录状态
    name = session.get('name')
    if name:
        return render_template('home.html', user_dict=users)
    else:
        return redirect('/login')

@app.route('/detail/<int:id>')
def detail(id):
    # 校验是否登录
    name = session.get('name')
    if not name:
        return redirect('/login')
    user_detail=users.get(id)
    return render_template('detail.html',user_detail=user_detail)

@app.route('/test')
def test():
    # 返回json示例
    return jsonify({'name':'lxj','age':18})


if __name__ == '__main__':
    app.run()

总结

"""
总结:
注册路由 app.route(路径,menhods=['GET','POST'])
1.获取数据与请求方式:        需要导入request
                            request.method 取请求方式
                            request.form   取数据
                            
2.返回页面:                需要创建一个templates文件夹,编写html文件

3.保存登录状态:             需要导入session,需要设置secret_key
                            session['key']=value 存储到session中
                            session.get('key') 从session中取值

4.模板渲染
                     -兼容django的dtl
        				-更强大,可以加括号,字典可以. | get | []   .values()   .items()
        				-{% for %}
            
5.路由可以加转换器 <int:id>
新式四件套:
    1.返回模板              导入render_template   
    2.重定向                导入redirect          
    3.返回字符串            直接return
    4.返回json              导入jsonify
    
"""

配置文件

django中有settings,任何项目都会有配置文件,使用方式不同。

flask 也有配置文件

1.测试阶段

from flask import Flask
app = Flask(__name__)


# flask所有的配置都放在app里,直接使用app对象获取配置信息
# 1.设置方式一
# app.debug= True  # 调试模式, 修改代码后不需要重启服务器,自动重启 提示错误信息更详细
# app.secret_key='213jdfssd11' # 设置session的密钥
# "都放在了app.config里"

# 2.设置方式二
# app.config['DEBUG']=True
# app.config['secret_key'] = '213jdfssd11'


# 3. 使用py文件
app.config.from_pyfile('settings.py')  #创建一个py文件,在里面修改

# 4. 常用的,类的方式,在settings里写3个类,开发用,测试用,上线用
app.config.from_object('settings.Development')  # 指定文件里的类
"""
settings.py内
class BASE(object):
    DEBUG = False

class Production(BASE):
    DATABASE_URI = 'mysql://user@localhost/foo'

class Development(BASE):
    DEBUG = True
    DATABASE_URI = 'localhost'

"""

@app.route('/')
def home():
    return 'hello world'



if __name__ == '__main__':
    app.run()


其他方法

    ### 其他:
    #通过环境变量配置
    # app.config.from_envvar("环境变量名称")

    # json
    # app.config.from_json("json文件名称")
    # JSON文件名称,必须是json格式,因为内部会执行json.loads


    # 字典格式---》配置中心
    # app.config.from_mapping({'DEBUG': True})
   

内置的配置字段

常用的
DEBUG
SECRET_KEY     
SESSION_COOKIE_NAME      # 网页cookie里session的名字
PERMANENT_SESSION_LIFETIME  # session过期时间设置
其他的可以自己写 
eg:
    redis的链接地址
    mysql的链接地址
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
 {
        'DEBUG':                                get_debug_flag(default=False),  是否开启Debug模式
        'TESTING':                              False,                          是否开启测试模式
        'PROPAGATE_EXCEPTIONS':                 None,                          
        'PRESERVE_CONTEXT_ON_EXCEPTION':        None,
        'SECRET_KEY':                           None,
        'PERMANENT_SESSION_LIFETIME':           timedelta(days=31),
        'USE_X_SENDFILE':                       False,
        'LOGGER_NAME':                          None,
        'LOGGER_HANDLER_POLICY':               'always',
        'SERVER_NAME':                          None,
        'APPLICATION_ROOT':                     None,
        'SESSION_COOKIE_NAME':                  'session',
        'SESSION_COOKIE_DOMAIN':                None,
        'SESSION_COOKIE_PATH':                  None,
        'SESSION_COOKIE_HTTPONLY':              True,
        'SESSION_COOKIE_SECURE':                False,
        'SESSION_REFRESH_EACH_REQUEST':         True,
        'MAX_CONTENT_LENGTH':                   None,
        'SEND_FILE_MAX_AGE_DEFAULT':            timedelta(hours=12),
        'TRAP_BAD_REQUEST_ERRORS':              False,
        'TRAP_HTTP_EXCEPTIONS':                 False,
        'EXPLAIN_TEMPLATE_LOADING':             False,
        'PREFERRED_URL_SCHEME':                 'http',
        'JSON_AS_ASCII':                        True,
        'JSON_SORT_KEYS':                       True,
        'JSONIFY_PRETTYPRINT_REGULAR':          True,
        'JSONIFY_MIMETYPE':                     'application/json',
        'TEMPLATES_AUTO_RELOAD':                None,
    }

路由系统

flask的路由是基于装饰器的(用的多),也可以写在py文件里。

回顾装饰器:

装饰器是什么?

装饰器的本质是闭包函数。被装饰以后。在执行被装饰的函数其实不是执行之前的函数了,执行的是装饰器返回的函数。所以才加入新的功能。

它的作用是在不改变程序原代码和调用方式的基础上为程序增加新功能。

本质是通过闭包函数实现。

装饰器语法糖 就是@ python的特殊语法。它有特殊作用 --把被装饰函数当做参数传入装饰器。并把把装饰器的执行结果赋值给 被装饰的函数。

app.route源码分析

# 路由的装饰器源码分析
	# 咱们这样写
    @app.route('/login')
    def index():
        pass
    1.本质是 index = app.route('/login')(index)
    2.app.route('/login')执行结果return了decorator函数
    3 等于执行了 decorator(index)
    
 def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
    """
    rule 是路径
    options 接收了其他参数
    """
        def decorator(f: T_route) -> T_route:
            "f 就是被装饰器当做参数传进来的index"
            endpoint = options.pop("endpoint", None) #options相当于一个字典,现在没有endpoint参数,那么endpoint = None
            # 核心  self就是app=flask(__name__)实例化得到的对象(flask对象),(flask继承了Scaffold)
            #  对象里有个方法add_url_rule,就是在添加路由
            self.add_url_rule(rule, endpoint, f, **options)
            return f

        return decorator   

不使用装饰器 自己注册路由的方法就是用app对象调用add_url_rule方法

app.add_url_rule('/',endpoint=None,view_func=home,methods=['GET'])

其实add_url_rule 类似 django中的path

flask 路由的本质其实就是用app对象的add_url_rule 方法完成路由注册

add_url_rule参数

重要
rule         # URL 路由规则(路径)
view_func      # 视图函数
defaults=None   # 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
endpoint=None,  # 路径的别名,名称,用于反向解析URL,和django路由注册中的name一样
methods=None    # 允许的请求方式,如:["GET", "POST"]
了解

strict_slashes = None  #对URL最后的 / 符号是否严格要求
    '''
        @app.route('/index', strict_slashes=False)
        #访问http://www.xx.com/index/ 或http://www.xx.com/index均可
        @app.route('/index', strict_slashes=True)
        #仅访问http://www.xx.com/index
    '''

#重定向到指定地址
redirect_to = None, 
    '''
        @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
    '''

转换器

 'default':  # 不写类型就是default  示例:/<pk>
 'string':          UnicodeConverter,
 'path':             PathConverter,
 'int':             IntegerConverter,

  了解  
'any':              AnyConverter,
'float':            FloatConverter,
'uuid':             UUIDConverter,