Flask(四) 中间件、模板、路由系统

中间件

# 实现中间件
class MiddleWare(object):
    def __init__(self, wsgi_app):
        self.wsgi_app = wsgi_app

    def __call__(self, *args, **kwargs):
        print('before')
        ret = self.wsgi_app(*args, **kwargs)
        print('after')

        return ret


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


# 源码片段
class Flask(_PackageBoundObject):
    def run(self, host=None, port=None, debug=None,
            load_dotenv=True, **options):
		...
        from werkzeug.serving import run_simple

        try:
            run_simple(host, port, self, **options)
        finally:
        	...

        def __call__(self, environ, start_response):
       		return self.wsgi_app(environ, start_response)

      	def wsgi_app(self, environ, start_response):
      		...


# 因为比before_request更靠前, 还没有request,需要自己处理environ

模板

# 模板文件目录默认templates, 可以在实列化时通过template_folder指定
# 示例
app = Flask(__name__, template_folder='templates')

# 静态文件目录默认static, 可以在实列化时通过static_folder指定, 
app = Flask(__name__, static_folder='static')

# 静态文件前缀(别名)通过static_url_path指定
app = Flask(__name__, static_url_path=None)

# 源码片段
class Flask(_PackageBoundObject):
	def __init__(
	        self,
	        import_name,
	        static_url_path=None,
	        static_folder='static',
	        static_host=None,
	        host_matching=False,
	        subdomain_matching=False,
	        template_folder='templates',
	        instance_path=None,
	        instance_relative_config=False,
	        root_path=None
	    ):
	    ...



# 渲染模板(语法和Django差别不大, 支持python语法)
from flask import render_template

# 示例
@app.route('/login/')
def login():
	return render_template('login.html', **{'key': 'value'})

# 原型
def render_template(template_name_or_list, **context):
	...
不同于django的是context参数接收关键字参数


###### 渲染普通变量 ######
# 后端
import time
@app.route('/test/')
def test():
    return render_template('test.html',
                           tm=time.strftime('%Y-%m-%d', time.localtime()))

# test.html
<h1>{{ tm }}</h1>

# 页面显示
2017-08-09

###### 注意 ######
# flask和django一样做了防xss攻击, 渲染的变量是html标签字符串时需要处理

--- 方式一 ---
# 视图
from flask import Markup
return render_template('login.html', btn=Markup('<input type="submit" value="登录" / >'))

# login.html
{{ btn }}


--- 方式二 ---
# 视图
return render_template('account/login.html', btn='<input type="submit" value="登录" / >')

# login.html
{{ btn }}

###### 渲染字典 ######
# 视图
dic = {
    1: {'name': 'a', 'gender': 'male', 'age': 18},
    2: {'name': 'b', 'gender': 'female', 'age': 18},
}
@app.route('/test1/')
    def test1():
        return render_template('test1.html', dic=dic)

# test1.html
<table>
    <thead>
    <tr>
        <th>id</th>
        <th>name</th>
        <th>age</th>
        <th>gender</th>
    </tr>
    </thead>
    <tbody>
    {% for k, v in dic.items() %}
    <tr>
        <td>{{ k }}</td>
        <td>{{ v.name }}</td>
        <td>{{ v['age'] }}</td>
        <td>{{ v.get('gender') }}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>

# 页面显示
id	name	age	gender
1	a	18	male
2	b	18	female



###### 渲染列表(元组) ######
# 视图
@app.route('/test2/')
def test2():
    return render_template('test2.html', li=[1, 2, 3])

# 模板
<p>{{ li.0 }}</p>
<p>{{ li[2] }}</p>
<p>{{ li[-1::-1] }}</p>
{% for i in li %}
<span>{{ i }}</span>
{% endfor %}

# 页面显示
1
3
[3, 2, 1]
1 2 3


###### 渲染函数 ######
# 不同于django, flask需要加括号调用
# 视图
def func():
    return '<h1>Hello Flask</h1>'
@app.route('/test3/')
    def test3():
        return render_template('test3.html', func=func)

# test3.html
<p>{{ func }}</p>
<p>{{ func() }}</p>
<p>{{ func.__name__ }}</p>

# 页面显示
<function func at 0x7f1c22d1fb70>
<h1>Hello Flask</h1>
func


###### 全局装饰器和全局过滤器 ######
# 同普通函数一样可以传参

# 视图
@app.template_global()
def hello():
    return 'Hello Flask!'

# 模板
{{ hello() }}

# 过滤器
# 视图
@app.template_filter()
def is_even(v, a, b):
    return not v % 2

# 模板
{% if 3|is_even('a', 'b') %}
    even
{% endif %}

###### 模板继承 ######
# 语法和django一样

# base.html父模板
{% block content %}
{% endblock %}

# 子模板
{% extends 'base.html' %}
{% block content %}
    ...
{% endblock %}


###### 包含模板 ######
{% include 'menu.html' %}


###### 宏 ######
{% macro generate_input(name, type='text', value='') %}
<p>
    <label for="id_{{ name }}">{{ name }}</label>
    <input type="{{ type }}" name="{{ name }}" value="{{ value }}" id="id_{{ name }}"/ >
</p>
{% endmacro %}

{{ generate_input('username') }}
{{ generate_input('password') }}

路由系统(本质就是url和函数对应关系)

###### 路由设置的两种方式 ######
*** 方式一 ***
@app.route('/index/')
def index():
    return index

*** 方式二 ***
app.add_url_rule('/index/', None, index)

# 别名(默认等于函数名) endpoint不能重名
@app.route('/index/', endpoint='n1')

# 别名反向生成url
from flask import url_for
url_for('n1')

# 源码片段
def route(self, rule, **options):
    def decorator(f):
        endpoint = options.pop("endpoint", f.__name__)
        self.add_url_rule(rule, endpoint, f, **options)
        return f
    return decorator

###### 参数 ######
rule url规则
'/index/'

view_func 视图函数名称
view_func='home'

defaults 指定默认传参
defaults={'k': 'v'}

endpoint 名称, 用于反向生成URL, 默认视图名称
endpoint='home'

methods 允许的请求方式,默认只允许GET
methods=['POST', 'GET']

strict_slashes 对URL最后的/符号是否严格匹配, 默认True
strict_slashes=True

redirect_to 重定向指定地址
redirect_to='/login/'

subdomain 指定子域名访问
subdomain='admin'
app.config['SERVER_NAME'] = 'ret.com:5000'


###### 动态路由 ######
# 获取匹配字符
@app.route('/index/<nid>/')

# 获取匹配整数
@app.route('/index/<int:nid>/')

def index(nid):
	return 'index'

url_for('index', nid=123)


# 自定义正则
from werkzeug.routing import BaseConverter

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

    def to_python(self, value):
        return value

    def to_url(self, value):
        return super(RegexConverter, self).to_url(value)


app.url_map.converters['reg'] = RegexConverter


@home.route('/index/<reg("\w{3}"):nid>/')

# 给视图添加自定义装饰器时,应该紧贴着放在函数上方
posted @ 2018-08-15 21:41  ret  阅读(130)  评论(0)    收藏  举报