Flask的路由是基于装饰器实现的,其本质是通过app.add_url_rule方法实现的
来看下面函数与路由在源码中的流程
from flask import Flask
app = Flask(__name__)
# 这里要注意: route加括号已经执行了
@app.route('/', methods=['GET', 'POST'], endpoint='haha') --> 已经变成: @decorator
def hello_world():
return 'Hello World!'
源码
route源码:
def route(self, rule, **options): self是Flask对象 即app, rule是我们写的路由, options是其余参数组成的字典
def decorator(f): f是视图函数
endpoint = options.pop("endpoint", None) 拿到别名并将别名从options字典中删除
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
add_url_rule源码:
@setupmethod
def add_url_rule(
self, # --> Flask对象,即app
rule, # --> 装饰器里的路由
endpoint=None, # --> 路由别名
view_func=None, # --> 视图函数
provide_automatic_options=None,
**options # 用来接收methods等参数的字典
):
if endpoint is None: # 若路由没有起别名
endpoint = _endpoint_from_view_func(view_func) # endpoint = 视图函数的名字
options["endpoint"] = endpoint # 将endpoint添加到options里
methods = options.pop("methods", None) # 获取methods的值
if methods is None:
# methods为空 则默认是GET请求
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)
required_methods = set(getattr(view_func, "required_methods", ()))
if provide_automatic_options is None:
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
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) # 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
)
# 若view_functions中有endpoint 则取出赋值给view_func
# view_func要么必须有值, 要么endpoint有别名, 最终endpoint的值也会赋值给view_func
self.view_functions[endpoint] = view_func
# 如果endpoint有别名 view_func = endpoint
# 如果endpoint没有有别名 view_func = endpoint(视图函数名字)
核心源码: route -> decorator -> add_url_rule
flask路由基于装饰器,本质是基于:add_url_rule
add_url_rule 源码中,endpoint如果为空,
endpoint = _endpoint_from_view_func(view_func),最终取view_func.__name__(函数名)
- @app.route('/user/<username>')
- @app.route('/post/<int:post_id>')
- @app.route('/post/<float:post_id>')
- @app.route('/post/<path:path>')
常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
路由的两种写法
- 典型装饰器写法
from flask import Flask
app = Flask(__name__)
# 路由装饰器配置该函数的路由
@app.route('/index/<int:nid>',methods=['GET'],endpoint='haha')
def index(nid):
return str(nid)
路由参数:
'/index/<int:nid>' : 路由有名分组,视图函数要接收
methods : 该视图函数可以用的请求方式
endpoint : 路由别名反向解析, 在其他视图函数中用: real_url=url_for("别名") 调用该函数
endpoint默认指定的是函数名字
- add_url_rule 写法
app.add_url_rule参数:
- rule, URL规则
- view_func, 视图函数名称
- defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
- endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
- methods = None, 允许的请求方式,如:["GET", "POST"]
- redirect_to = '/login' 重定向到指定的地址 可以是路径也可以是别名
- strict_slashes 严格模式
@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
案例:
def index(nid):
return nid
app.add_url_rule(
'/index/<string:nid>', # url
view_func=index, # 该路由关联的函数
endpoint="haha", # 路由别名
methods=['POST',"GET"] # 允许的请求方式
)
补充: 别名使用
from flask import Flask, request, render_template, redirect, url_for
app = Flask(__name__)
# 路由装饰器配置该函数的路由
@app.route('/login/<string:nid>',methods=['GET'],endpoint='haha')
def login(nid):
return render_template('login.html', nid=nid)
def index(nid):
url = url_for('haha', nid=nid) # 通过路由别名反向生成路由
print(url) # /login/1
return redirect(url) # 重定向到login路由对应的函数
app.add_url_rule('/index/<string:nid>',view_func=index,methods=['POST',"GET"])
if __name__ == '__main__':
app.run()
补充: 反向生成路由时也可以携带参数
注意: 别名不能重复,一个路由一个别名
路由扩展 支持正则
Flask路由默认不支持正则匹配,我们可以自定义路由扩展使其支持正则:
1.写类,继承BaseConverter
2 注册:app.url_map.converters['regex'] = RegexConverter
3 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中
from flask import Flask, url_for
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
"""
#value就正则匹配出来的结果
# print('value',value,type(value))
return value
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
"""
val = super(RegexConverter, self).to_url(value)
# print(val) # 666
return val
app.url_map.converters['regex'] = RegexConverter
@app.route('/test/<regex("\d+"):nid>',endpoint="tes")
def test(nid):
print("nid",nid,type(nid))
print(url_for('tes', nid='666')) # /test/666
return 'test'
配置文件
方式一:
这中方式只能配置两种:
app.debug=True
app.secret_key="123123"
方式二:
使用config字典:
app.config['DEBUG']=True
方式三:
导入文件(插拔式) ,推荐使用
- settings.py
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
DEBUG = True
class TestingConfig(Config):
TESTING = True
- 视图函数文件.py
from flask import Flask
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
补充:
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,
}