
正文
flask_之URL
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
URL篇
在分析路由匹配过程之前,我们先来看看
flask
中,构建这个路由规则的两种方法:
-
通过
@app.route()decorator -
通过
app.add_url_rule,这个方法的签名为add_url_rule(self, rule, endpoint=None, view_func=None, **options),参数的含义如下:-
rule: url 规则字符串,可以是静态的/path,也可以包含/ -
endpoint:要注册规则的 endpoint,默认是view_func的名字 -
view_func:对应 url 的处理函数,也被称为视图函数
-
这两种方法是等价的,也就是说:
@app.route('/')
def hello():
return "hello, world!"
也可以写成
def hello():
return "hello, world!" app.add_url_rule('/', 'hello', hello)
其实,还有一种方法来构建路由规则——直接操作
app.url_map
这个数据结构。不过这种方法并不是很常用,因此就不展开了
静态路由
@app.route('/')
def hello_world():
# 变量可以通过赋值传到前端,前端可以通过Jinja语法{{}}渲染
return render_template('t1.html', name='t1', age=16)
@app.route('/services')
def services():
return 'Services'
@app.route('/about')
def about():
return 'About'
# 相对projects解释类似于文件夹解释形式,指向某一个文件夹下的某个文件
@app.route('/projects/')
@app.route('/projects_our') # 可以定义多个URL到同一个视图函数上,Flask支持
def projects():
return 'Projects'
@app.route('/login',methods=["GET","POST"])
def login():
return render_template('login.html', req_method=request.method)
动态路由
# 动态路由
@app.route('/user/<username>')
def user(username):
print username
return username
# 路由转换器:指定参数类型
# flask提供3种:int(整形)|float(浮点型)|path(路径,并支持一个/)
@app.route('/user/<int:user_id>')
def user(user_id):
print user_id
return 'User_id:%s'%user_id
自定义路由规则
# flask不提供正则表达的形式的URL匹配
# 可通过定义完成
# 1、from werkzeug.routing import BaseConverter
# 2、自定义类
#转换器
class RegexConverter(BaseConverter):
def __init__(self,url_map,*items):
super(RegexConverter,self).__init__(self)
# print items # (u'[a-z]{3}[A-Z]{3}',)
# print url_map # URL 的一个MAP对象,类似路由表
self.regex = items[0]
# 3、要将定义的类注册到APP的url_map中,定义名称
# app.url_map.converters['regex'] = RegexConverter
# 4、使用
@app.route('/user/<regex("[a-z]{3}[A-Z]{3}"):username>')
def user(username):
print username
return 'Username:%s' % username
浅析源码
@app.route('/')
def hello_world():
# 变量可以通过赋值传到前端,前端可以通过Jinja语法{{}}渲染
return render_template('t1.html', name='t1', age=16)
@app.route('/services')
def services():
return 'Services'
@app.route('/about')
def about():
return 'About'
# 相对projects解释类似于文件夹解释形式,指向某一个文件夹下的某个文件
@app.route('/projects/')
@app.route('/projects_our') # 可以定义多个URL到同一个视图函数上,Flask支持
def projects():
return 'Projects'
@app.route('/login',methods=["GET","POST"])
def login():
return render_template('login.html', req_method=request.method)
# 动态路由
@app.route('/user/<username>')
def user(username):
print username
return username # 路由转换器:指定参数类型
# flask提供3种:int(整形)|float(浮点型)|path(路径,并支持一个/)
@app.route('/user/<int:user_id>')
def user(user_id):
print user_id
return 'User_id:%s'%user_id
自定义路由规则
# flask不提供正则表达的形式的URL匹配
# 可通过定义完成
# 1、from werkzeug.routing import BaseConverter
# 2、自定义类
#转换器
class RegexConverter(BaseConverter):
def __init__(self,url_map,*items):
super(RegexConverter,self).__init__(self)
# print items # (u'[a-z]{3}[A-Z]{3}',)
# print url_map # URL 的一个MAP对象,类似路由表
self.regex = items[0]
# 3、要将定义的类注册到APP的url_map中,定义名称
# app.url_map.converters['regex'] = RegexConverter
# 4、使用
@app.route('/user/<regex("[a-z]{3}[A-Z]{3}"):username>')
def user(username):
print username
return 'Username:%s' % username
浅析源码
# flask不提供正则表达的形式的URL匹配
# 可通过定义完成
# 1、from werkzeug.routing import BaseConverter
# 2、自定义类
#转换器
class RegexConverter(BaseConverter):
def __init__(self,url_map,*items):
super(RegexConverter,self).__init__(self)
# print items # (u'[a-z]{3}[A-Z]{3}',)
# print url_map # URL 的一个MAP对象,类似路由表
self.regex = items[0] # 3、要将定义的类注册到APP的url_map中,定义名称
# app.url_map.converters['regex'] = RegexConverter # 4、使用
@app.route('/user/<regex("[a-z]{3}[A-Z]{3}"):username>')
def user(username):
print username
return 'Username:%s' % username
注册路由规则的时候,flask 内部做了哪些东西呢?我们来看看
route
方法:
def route(self, rule, **options):
"""A decorator that is used to register a view function for a
given URL rule. This does the same thing as :meth:`add_url_rule`
but is intended for decorator usage.
""" def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f return decorator
route
route
方法内部也是调用
add_url_rule
,只不过在外面包了一层装饰器的逻辑,这也验证了上面两种方法等价的说法
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
"""Connects a URL rule. Works exactly like the :meth:`route`
decorator. If a view_func is provided it will be registered with the
endpoint.
"""
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
methods = options.pop('methods', None) # if the methods are not given and the view_func object knows its
# methods we can use that instead. If neither exists, we go with
# a tuple of only ``GET`` as default.
if methods is None:
methods = getattr(view_func, 'methods', None) or ('GET',)
if isinstance(methods, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods) # Methods that should always be added
required_methods = set(getattr(view_func, 'required_methods', ())) # starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling.
provide_automatic_options = getattr(view_func,
'provide_automatic_options', None) if provide_automatic_options is None:
if 'OPTIONS' not in methods:
provide_automatic_options = True
required_methods.add('OPTIONS')
else:
provide_automatic_options = False # Add the required methods now.
methods |= required_methods rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options self.url_map.add(rule)
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError('View function mapping is overwriting an '
'existing endpoint function: %s' % endpoint)
self.view_functions[endpoint] = view_func
add_url_rule
上面这段代码省略了处理 endpoint 和构建 methods 的部分逻辑,可以看到它主要做的事情就是更新
self.url_map
和
self.view_functions
两个变量。找到变量的定义,发现
url_map
是
werkzeug.routeing:Map
类的对象,
rule
是
werkzeug.routing:Rule
类的对象,
view_functions
就是一个字典。这和我们之前预想的并不一样,这里增加了
Rule
和
Map
的封装,还把
url
和
view_func
保存到了不同的地方。
需要注意的是:每个视图函数的 endpoint 必须是不同的,否则会报
AssertionError
。
未完待续。。。






