2. Flask 配置、路由、模版、装饰器等

Flask 配置、路由、模版、装饰器等

wsgi 找源码的流程

"""
    1.程序启动,等待用户请求到来
        app.run()
    2.用户请求到来 app()  
        app.__call__   # return self.wsgi_app(environ, start_response)
"""

from flask import Flask

app = Flask(__name__)

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

if __name__ == '__main__':
    app.run()
# wsgi 找源码的流程
from werkzeug.serving import run_simple
from werkzeug.wrappers.response import Response
from werkzeug.wrappers import Response as ResponseBase

def func(environ, start_response):
    print('请求来了')
    response = Response('hello world')
    # response = ResponseBase('hello world')
    return response(environ, start_response)

if __name__ == '__main__':
    run_simple('127.0.0.1', 5000, func)
追踪过程如下:
app.run()---> run_simple()中的self == app()---> __call__ --->  wsgi_app()---> full_dispatch_request()----> finalize_request(rv)----> make_response(rv)----> response_class()---> Response(ResponseBase)----> from werkzeug.wrappers import Response as ResponseBase

#### flask对象-静态文件的处理

```python
from flask import Flask,render_template

app = Flask(__name__,template_folder='templates',static_folder='static',static_url_path='/yy')
# static_folder  指定的静态文件所在的目录匹配目录名    此'static'特指 static_folder定义的目录
# static_url_path 指定静态文件访问匹配的路径代替static_folder目录名,类似别名   
# 使用 static_url_path 和 {{ url_for('static',filename='img/haha.png')}} 可随意变更静态文件目录不用同时变量html
@app.route('/img')
def img():
    return render_template('img.html')

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




    
    templates and static


    

templates and static

{# 建议 #}

配置文件

基于全局变量
from flask import Flask

app = Flask(__name__)

# 加载配置文件
app.config.from_object('config.settings')    # 加载 ./config/settings.py  配置文件 settings.py 调用本地localsettings.py 配置,配置环境隔离,与敏感数据分离

print(app.config['DB_HOST'])

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

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

# ./config/settings.py
# 通用全局数据
DB_HOST = 'home.vimll.com'
DB_PORT = 'xxxxxx'
DB_USER = 'root'
DB_PWD = '111111'

# localsettings.py 本地测试或者敏感数据使用,本地环境配置覆盖上述全局通用配置
try:
    from .localsettings import *
except ImportError:
    pass

# ./config/localsettings.py
# localsettings.py 本地环境配置覆盖 settings.py内的全局通用配置
# 敏感数据放在正式环境本地的 localsettings.py

DB_HOST = '127.0.0.1'
DB_PORT = '3306'
DB_USER = 'root'
DB_PWD = '111111'

基于类的方式

from flask import Flask

app = Flask(__name__)

app.config.from_object('config.settings')

# 加载基础配置文件
app.config.from_object('config.settings.BaseSettings')   # dev pro 配置包含BaseSettings
# 加载开发环境配置文件
app.config.from_object('config.settings.DevSettings')
# 加载生产环境配置文件
app.config.from_object('config.settings.ProSettings')

print(app.config['DB_HOST'])
print('BaseSettings', app.config['A'])
print('DevSettings', app.config['B'])
print('ProSettings', app.config['C'])

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

if __name__ == '__main__':
    app.run()
127.0.0.1
BaseSettings 1
DevSettings 2
ProSettings 3
 * Serving Flask app '__main__'
 * Debug mode: off

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
# 通用全局数据
DB_HOST = 'home.vimll.com'
DB_PORT = 'xxxxxx'
DB_USER = 'root'
DB_PWD = '111111'

# 基于类的配置文件
class BaseSettings(object):
    """通用基础配置文件"""
    A = '1'

class DevSettings(BaseSettings):
    """开发环境配置文件"""
    B = '2'

class ProSettings(BaseSettings):
    """生产环境配置文件"""
    C = '3'

# localsettings.py 本地测试或者敏感数据使用,本地环境配置覆盖上述全局通用配置
try:
    from .localsettings import *
except ImportError:
    pass

if __name__ == '__main__':
    obj = DevSettings()
    print(obj.A)

路由系统

路由加载的两种方式:
  • app.add_url_rule('/index', 'index', index)
  • @app.route('/index')
路由加载的源码流程:
  • 将url和函数打包成为 rule 对象
  • 将rule对象添加到map对象中。
  • app.url_map = map对象
from flask import Flask
app = Flask(__name__,template_folder='templates',static_folder='static',static_url_path='/static')

app.add_url_rule('/index', 'index', index)
# @app.route('/index')   # self.add_url_rule(rule, endpoint, f, **options)
def index():
    return '首页'

if __name__ == '__main__':
    app.run()
动态路由
@app.route('/login')
def login():
    return render_template('login.html')

@app.route('/login/')
def login(name):
    print(type(name))
    return render_template('login.html')

@app.route('/login/')
def login(name):
    print(type(name))
    return render_template('login.html')
正则表达式的路由
from werkzeug.routing import BaseConverter
from flask import Flask, render_template

app = Flask(__name__)

class RegConverter(BaseConverter):
    def __init__(self, map, regex):
        super().__init__(map)
        self.regex = regex

    # 对匹配到的值进行处理的函数(父类中有)
    def to_python(self, value):
        # return value  默认匹配得到的是字符串
        return int(value)  # 将其变成数值型

app.url_map.converters['regex'] = RegConverter

@app.route('/index/')
def index(x1):
    print(x1, type(x1))     # 1 
    return "正则表达式的路由"

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

视图

FBV
from flask import Flask, render_template
app = Flask(__name__, template_folder='templates',
            static_folder='static', static_url_path='/static')

@app.route('/login')
def index():
    return render_template('login.html')

if __name__ == '__main__':
    app.run()
CBV
from flask import Flask,render_template,views

app = Flask(__name__,)

def test1(func):
    def inner(*args,**kwargs):
        print('before1')
        result = func(*args,**kwargs)
        print('after1')
        return result
    return inner

def test2(func):
    def inner(*args,**kwargs):
        print('before2')
        result = func(*args,**kwargs)
        print('after2')
        return result
    return inner

class UserView(views.MethodView):
    methods = ['GET',"POST"]

    decorators = [test1,test2]

    def get(self):
        print('get')
        return 'get'

    def post(self):
        print('post')
        return 'post'

app.add_url_rule('/user', view_func=UserView.as_view('user')) # endpoint

if __name__ == '__main__':
    app.run()
* Serving Flask app '__main__'
 * Debug mode: off

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [14/May/2024 17:24:43] "GET /user HTTP/1.1" 200 -

before2
before1
get
after1
after2

127.0.0.1 - - [14/May/2024 17:24:50] "POST /user HTTP/1.1" 200 -

before2
before1
post
after1
after2

模板

基本用法

flask比django更加接近Python

from flask import Flask,render_template

app = Flask(__name__,)

def func(arg):
    return '你好' + arg

@app.route('/md')
def index():
    nums = [11,222,33]
    return render_template('md.html',nums=nums,f=func)

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



    
    Title


    

{% block content %} {% endblock %}

{% extends 'layout.html' %}

{% block content %}
    

MD

{% include 'form.html' %} {{ f("waws") }} {% endblock %}
定义全局模板方法

注意:在蓝图中注册的时候,应用返回只有本蓝图有效

from flask import Flask,render_template

app = Flask(__name__,)

@app.template_global() #  {{ func("AAA") }}
def func(arg):
    return arg

@app.template_filter() # {{ "AAA"|x1("-BBB") }} waws默认作为x1的第一个参数arg、在x1()中的参数使用name进行接收
def x1(arg,name):
    return arg + name

@app.template_filter() # {{ "AAA"|x1("-BBB")|x2("-CCC") }}    # AAA-BBB-CCC
def x2(arg,name):
    return arg + name

@app.route('/md/hi')
def index():
    return render_template('md_hi.html')

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

特殊装饰器

特殊装饰器before_reques after_request

注意:before_after request可以在蓝图中定义,在蓝图中定义的话,作用域只在本蓝图。

from flask import Flask,render_template,request

app = Flask(__name__)

@app.before_request
def f1():
    if request.path == '/login':
        return
    print('f1')
    # return '123'   # 如果有返回值,就不会往后继续执行

@app.after_request
def f10(response):       # 注意在after_request中参数和返回值都是response
    print('f10')
    return response

@app.route('/index')
def index():
    print('index')
    return '首页'

@app.route('/login')
def login():
    print('login')
    return '登录'

if __name__ == '__main__':
    app.run()
* Serving Flask app '__main__'
 * Debug mode: off

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [15/May/2024 09:36:54] "GET /index HTTP/1.1" 200 -

f1
index
f10

127.0.0.1 - - [15/May/2024 09:37:12] "GET /login HTTP/1.1" 200 -

login
f10
多个特殊装饰器
from flask import Flask,render_template,request

app = Flask(__name__)

@app.before_request
def f1():
    print('f1')

@app.before_request
def f2():
    print('f2')

@app.after_request
def f10(response):
    print('f10')
    return response

@app.after_request
def f20(response):
    print('f20')
    return response

@app.route('/index')
def index():
    print('index')
    return '首页'

if __name__ == '__main__':
    app.run()
    app.__call__   
* Serving Flask app '__main__'
 * Debug mode: off

WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
127.0.0.1 - - [15/May/2024 09:47:22] "GET /index HTTP/1.1" 200 -

f1
f2
index
f20
f10
特殊装饰器的另一种使用方式
from flask import Flask,render_template

app = Flask(__name__,)

@app.route('/index')
def index():
    print('index')
    return '首页'

@app.before_request
def func():
    print('xxx')

def x1():
    print('X1')

app.before_request(x1)

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