1+1=10

扬长避短 vs 取长补短

Python flask入门笔记(五)

接触flask这几天,一直在遇到endpoint 这个东西。不能一直跳过它...

endpoint

例子

在前面,一直在使用装饰器route()

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "Hello World!"

这个写法等价于显式使用endpoint的:

from flask import Flask

app = Flask(__name__)

@app.route('/', endpoint='index')
def index():
    return "Hello World!"

如果不用装饰器,也可以用add_url_rule写成:

from flask import Flask

app = Flask(__name__)

def index():
    return "Hello World!"

app.add_url_rule('/', 'index', index)

更容易理解的,可能是写成这样?:

from flask import Flask

app = Flask(__name__)

def index():
    return "Hello World!"

app.add_url_rule('/', 'index')
app.view_functions['index'] = index

从这儿可以看到:

  • URL rule 和 endpoint 是一组
  • endpoint 和 view_func函数 是另一组

route() 参数

而不管装饰器route,还是函数add_url_rule,都接收如下参数:

  • rule:URL规则
  • endpoint:一个字符串,代表URL规则。Flask默认使用view_func的名字作为endpoint。
  • view_func:endpoint对应的view_func。一个view_func可以对应多个endpoint。装饰器route()不用指定这个参数,会自动生成。add_rul_rule()如果不指定,需要手动修改view_functions将其加入进去。
  • defaults:一个dict,为变量定义默认值。
  • subdomain:子域名,与应用配置SERVER_NAME相关。
  • **options:其他选项,传递给Rule对象,通过关键字参数指定。常用的有 method

url_map 与 view_functions

Flask内部维护了两张表:

  • url_map:url与endpoint的关系
  • view_functions:endpoint与function的关系

要作为view_functions的key,endpoint需要是唯一的(等同于一个标识符)。多个不同的endpoint可以对应同一个view_func

看一个例子

from flask import Flask

app = Flask(__name__)

@app.route('/')
@app.route('/debao')
def index():
    return "Hello World!"

print(app.url_map)
print(app.view_functions)

其控制台,结果:

Map([<Rule '/static/<filename>' (GET, HEAD, OPTIONS) -> static>,
 <Rule '/debao' (GET, HEAD, OPTIONS) -> index>,
 <Rule '/' (GET, HEAD, OPTIONS) -> index>])

{'static': <function Flask.__init__.<locals>.<lambda> at 0x000001CBABC6DBC0>, 'index': <function index at 0x000001CBAE6E7600>}

从这儿可以看到:

  • URL到"index"这个endpoint有两个规则
  • "index"这个endpoint到view有一个规则

  • flask有夹带私货:把静态资源static 规则默认添加进去了

url_for()

这个函数接收的第一个参数是 endpoint。用于反向构建URL字符串。

 url_for(endpoint, *, _anchor=None, _method=None, _scheme=None, _external=None, **values)

这个函数也挺复杂,受SERVER_NAME等一堆东西影响...

Werkzeug

Werkzeug,/ˈvɛrkʦɔyk/,德语,工具的意思。好吧,已经好几天了,看着音标我还是不会发这个音。

其实到现在为止,我还是没理解到endpoint的设计精髓,只是看到它把URL和对应处理函数view_func的映射拆成两部分,有助于解耦。

换个角度看:URL到 endpoint的映射,是wekzeug框架的内容;而endpoint到view函数的映射,是属于Flask自己内容。真要找答案,后面需要了解werkzeug才行

from werkzeug.routing import Map, Rule

url_map = Map([
    Rule('/', endpoint='blog/index'),
    Rule('/<int:year>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
    Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
    Rule('/about', endpoint='blog/about_me'),
    Rule('/feeds/', endpoint='blog/feeds'),
    Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
])

回到flask,还可以这么玩(先建立URL和endpoint关系,再使用endpoint装饰器):

from flask import Flask
from werkzeug.routing import Rule

app = Flask(__name__)
app.url_map.add(Rule('/', endpoint='index'))

@app.endpoint('index')
def my_index():
    return "Hello world"

参考

  • https://github.com/pallets/flask
  • https://flask.palletsprojects.com
  • https://werkzeug.palletsprojects.com/en/3.0.x/routing/

Comments