add_url_rule和app.route原理剖析
add_url_rule
add_url_rule(rule,endpoint=None,view_func=None)
这个方法用来添加url与视图函数的映射,如果没有填写,那么默认会使用"view_func"的名字作为"endpoint",以后再使用"url_for"的时候,就要看在映射的时候有没有传递"endpoint"参数,如果传递了,那么就应该使用"endpoint"指定的字符串,如果没有传递,那么就应该使用"view_func"的名字。
endpoint:可以配合url_for使用,并为url取一个名字
def test():
return "这是一个测试,测试链接:{}".format(url_for("my_test"))
app.add_url_rule(rule="/test/",view_func=test,endpoint="my_test")
app.route
app.route(rule,**options)
这个装饰器,其实也是使用"add_url_rule"来实现url与视图函数映射的。
@app.route('/',endpoint="test")
def index():
return '测试链接1:{}'.format(url_for("test"))
注意使用了,endpoint之后,原本的url_for("index")
就不能使用了,替换为了url_for("test")
标准类视图及其使用场景
类视图
之前的视图都是函数,所以一般简称为视图函数。其实视图也可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图好需要通过 app.add_urlrule(urlrule,view_func)
。
以下将对两种类视图进行讲解:
标准视图
标准视图继承自flask.views.View
,并且在子类中必须实现 dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者子类的对象,以下将用一个例子进行讲解:
app.py
from flask import Flask, views, request, render_template
app = Flask(__name__)
class BaseView(views.View):
# 自定义方法,用来获取模板路径
def get_template_name(self):
raise NotImplementedError()
# 必须实现的方法,用来处理请求的
def dispatch_request(self):
if request.method != 'GET':
return 'method error'
# 这想从self.get_data()中获取函数,子类应该实现这个方法
context = {'data': self.get_data()}
return render_template(self.get_template_name(), **context)
class TestView(BaseView):
# 实现从父类继承的获取模板路径的方法
def get_template_name(self):
return 'test.html'
# 重写获取函数的方法
def get_data(self):
return [{
'bool': 'false',
'address': 'http://www.baidu.com',
}]
# 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
app.add_url_rule('/test/',view_func=TestView.as_view('testview'))
if __name__ == '__main__':
app.run()
# 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
这是一个测试html,测试值{data:{{ data }}}
</body>
</html>
基于调度方法的视图:
Flask提供了另外一种类视图flask.views.MethodView
,对每个HTTP方法执行不同的函数(映射到对应的方法的小写的同名方法上),还对 RESTful API尤其有用。
栗子:
class TestAPI(views.MethodView):
#客户端通过get方法进行访问的时候执行的函数、
def get(self):
return jsonify({
'name':'angle',
'address':'http://www.baidu.com',
})
# 当客户端通过post方法进行访问的时候执行的函数
def post(self):
return 'post 请求'
app.add_url_rule(rule='/test/',view_func=TestAPI.as_view('test_api_view'))
用类视图的一个缺陷就是比较难用装饰器来装饰,比如有时候需要做权限验证的时候,比如:
def user_required(f)
def decorator(*args,**kwargs):
if not g.user:
return 'auth failure'
return f(*args,**kwargs)
return decorator
如果要在类视图上进行装饰,只能在as_view函数上进行装饰了,使用方式:
view = user_required(UserAPI.as_view('users'))
app.add_url_rule('/users/',views_func=view)
从flask 0.8 开始,还可以通过在类中添加decorators属性来实现对视图的装饰:
class UserAPI(views.MethodView):
decorator = [user_required]
....
推荐使用这种方法
标准类视图
标准类视图,必须继承来自"flask.views.View"
必须实现"dipatch_request"方法,以后请求过来后,都会执行这个方法,这个方法的返回值就相当于是之前的函数视图一样,也必须返回"Response"或者子类的对象,或者是字符串,或者是元组
必须通过"app.add_url_rule(rule,endpoint,view_func)"来做url与视图的映射。'view_func'这个参数,需要使用类视图下的'as_view'类方法类转换:TestView.as_view('test')
如果指定了"endpoint",那么在使用"url_for"反转的时候就必须使用"endpoint"指定的那个值,如果没有指定"endpoint",那么就可以使用"as_view(视图名字)"中指定的视图名字作为反转
类视图有以下好处:可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来使用就可以了,但是也不是说所有的视图都要使用类视图,这个要根据情况而定
class TestView(views.View):
def dispatch_request(self):
return "test view"
# 类.as_view(name) 返回一个函数,name:视图名
# endpoint没指定,就是用view_func名
app.add_url_rule('/test/',endpoint='test',view_func=TestView.as_view('test'))
# 有几个url需要返回json数据,“需要用jsonify”
class JSONView(views.View):
def get_data(self):
raise NotImplementedError
def dispatch_request(self):
return jsonify(self.get_data())
# #将视图函数中返回的字典,转换成json对象,然后返回
# class JSONResponse(Response):
#
# @classmethod
# def force_type(cls, response, environ=None):
# """
# 这个方法只有视图函数返回非字符,非元组,非Response对象才会调用
# :param response:
# :param environ:
# :return:
# response,视图函数的返回值
# """
# print(response)
# if isinstance(response,dict):
# # 转换
# response = jsonify(response)
# return super(JSONResponse,cls).force_type(response,environ)
# 添加response类,一定要添加
# app.response_class = JSONResponse
# 必须继承自views
class TestView(JSONView):
def get_data(self):
return {"username":"angle"}
app.add_url_rule('/test/',endpoint='test',view_func=TestView.as_view('test'))
from flask import Flask, views, request, render_template, jsonify
import json
app = Flask(__name__)
class ADSView(views.View):
def __init__(self):
super(ADSView,self).__init__()
self.context = {
'ads':"广告"
}
# 提取共同变量
class LoginView(ADSView):
def dispatch_request(self):
self.context.update({
'username':'login',
})
# 保证字典的中文不乱码
return json.dumps(self.context, ensure_ascii=False)
class RegistView(ADSView):
def dispatch_request(self):
return json.dumps(self.context, ensure_ascii=False)
app.add_url_rule('/ads/',endpoint='ads',view_func=ADSView.as_view('ads'))
app.add_url_rule('/login/',endpoint='login',view_func=LoginView.as_view('login'))
app.add_url_rule('/regist/',endpoint='regist',view_func=RegistView.as_view('regist'))
if __name__ == '__main__':
app.run()
类视图中使用装饰器
如果使用的是函数视图,那么自己定义的装饰器必须放在"app.route"下面,否者装饰器失效。
from flask import Flask,request
from functools import wraps
app = Flask(__name__)
def login_required(func):
@wraps(func)
def wrapper(*args,**kwargs):
username = request.args.get("username")
if username and username == "code":
return func(*args,**kwargs)
else:
return "请先登录"
return wrapper
@app.route('/settings/')
@login_required
def settings():
return "这是设置界面"
if __name__ == '__main__':
app.run(debug=True)
类视图的装饰器,需要重写类视图的一个类属性"decorators",这个类属性是一个列表或者元组都可以,里面装的是装饰器。
class ProfileView(views.View):
# 装饰器
decorators = [login_required]
def dispatch_request(self):
return "个人中心界面"
app.add_url_rule(rule='/profile/',view_func=ProfileView.as_view('profile'))
蓝图
作用
蓝图的作用就是让flask项目更加模块化,结构更加清晰,可以将相同模块的视图函数放在同一个蓝图下,同一个文件中,方便管理。
基本使用
如果想要某个蓝图下的所有url都有一个url前缀,那么可以定义蓝图的时候,指定url_prefix参数
demo1.py
from flask import Blueprint
demo1_bp = Blueprint("demo1",__name__,url_prefix='/demo1')
@demo1_bp.route('/')
def index():
return "this is demo1"
demo2.py
from flask import Blueprint
demo2_bp = Blueprint("/demo2",__name__,url_prefix='/demo2')
@demo2_bp.route('/')
def index():
return "this is demo2"
app.py
from flask import Flask,Blueprint
from test3.demo1 import demo1_bp
from test3.demo2 import demo2_bp
app = Flask(__name__)
app.register_blueprint(demo1_bp)
app.register_blueprint(demo2_bp)
@app.route('/')
def index():
return "this is app"
if __name__ == '__main__':
app.run(debug=True)
模版文件寻找规则
蓝图通过Blueprint函数中的template_folder参数进行修改模板文件的默认位置。
如果项目中的templates文件夹中有相应的模板文件,就直接使用了
如果项目中的templates文件夹中没有相应的模板文件,那么就到在定义蓝图的时候指定的路径中寻找,并且蓝图中的指定可以为相对路径,相对的是当前这个蓝图的文件所在的目录
如下:
demo1_bp = Blueprint("demo1",__name__,url_prefix='/demo1',template_folder="test_template")
因为这个蓝图文件是在test3/demo1.py,那么就会到test3文件夹下的test_template文件夹中寻找模板文件
静态文件寻找规则
在模板文件中,加载静态文件,如果使用url_for('static'),只会在app模板文件夹目录下查找静态文件
如果在加载静态文件的时候,指定的蓝图的名字,比如:'demo1.static',那么就会到这个蓝图指定的static_forder下查找静态文件
如下:
目录
demo1.py
from flask import Blueprint, render_template
demo1_bp = Blueprint("demo1",__name__,url_prefix='/demo1',template_folder="test_template",static_folder="test_static")
@demo1_bp.route('/')
def index():
return render_template('demo1.html')
test/test_template/demo1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{{ url_for('demo1.static',filename='demo1.css')}}">
</head>
<body>
<button>点击</button>
</body>
</html>
test/test_static/demo1.css
button{
background-color: yellow;
}
url_for反转蓝图注意事项
如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,那么就应该在使用url_for的时候就指定这个蓝图。比如news.news_list,否则就找不到这个endpoint,在模板中的url_for同样也是满足这个条件,就是指定蓝图的名字。
即使在同一蓝图中反转视图函数,也要指定蓝图的名字。
app.py
@app.route('/')
def index():
return render_template("index.html")
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="{{ url_for('demo1.index') }}">点击</a>
</body>
</html>
子域名实现详解
在创建蓝图对象的时候,需要传递一个"subdomain"参数,来指定这个子域名的前缀,例如
需要在主app文件中,需要配置app.config的SERVER_NAME参数,例如:
app.config['SERVER_NAME'] = 'test.com:5000'
demo1_bp = Blueprint("demo1",__name__,
subdomain='demo1',
url_prefix='/demo1',
template_folder="test_template",
static_folder="test_static")
注意ip地址不能有子域名,localhost也不能有子域名
在"C:\Windows\System32\drivers\etc"文件中,打开hosts文件,然后添加域名与本机的映射,例如
127.0.0.1 test.com
127.0.0.1 demo1.test.com