add_url_rule和app.route原理剖析
add_url_rule
add_url_rule(rule,endpoint=None,view_func=None)
这个方法用来添加url与视图函数的映射。
如果没有填写endpoint,那么默认会使用view_func的名字作为endpoint,
有填写endpoint,使用endpoint指定的字符串作为view_func函数的别名。
在url_for调用时应使用endpoint传递的别名
@app.route('/',endpoint='index') def hello_world(): # return 'Hello World!' + url_for('my_list') return 'Hello World!'+url_for('alias') def my_list(): return '我是列表页' app.add_url_rule('/list/',endpoint='alias',view_func=my_list) # app.test_request_context # 请求上下文 with app.test_request_context(): print(url_for('index')) #结果是:/ if __name__ == '__main__': app.run(debug=True)
add_url_rule相当于把/list/和my_list关联起来。endpioint相等于给视图函数命名一个别名。
def my_list(): return '我是列表页' app.add_url_rule('/list/',endpoint='alias',view_func=my_list) #和下面的等价: @app.route('/list/',endpoint='alias') def my_list(): return '我是列表页'
app.route原理
@app.route('/')装饰器底层是使用add_url_rule来实现url与视图函数映射的,
标准类视图及其使用场景
标准视图继承自flask.views.View,并且在子类中必须实现dispatch_request方法,这个方法类似于视图函数,也要返回一个基于Response或者其子类的对象。
- 标准类视图,必须继承自flask.views.View.
- 必须实现dipatch_request方法,当请求过来后会执行该方法且方法的返回值和之前的函数视图返回值一样,必须返回Response或者子类的对象,或者是字符串,或者是元组。
- 必须通过app.add_url_rule(rule,endpoint,view_func)来做url与视图的映射。view_func这个参数,需要使用类视图下的as_view类方法类转换:ListView.as_view('list')。
- 如果指定了endpoint,那么在使用url_for反转的时候就必须使用endpoint指定的别名。如果没有指定endpoint,那么就可以使用as_view(视图名字)中指定的视图名字来作为反转。
- 类视图有以下好处:可以继承,把一些共性的东西抽取出来放到父视图中,子视图直接拿来用。但是也不是说所有的视图都要使用类视图,这个要根据情况而定。
# 有几个url需要返回json数据 # 有几个视图,是需要返回相同的变量 class JSONView(views.View): def get_data(self): raise NotImplementedError def dispatch_request(self): return jsonify(self.get_data()) class ListView(JSONView): def get_data(self): return {"username":'wqbin','password':'123456'} app.add_url_rule('/list/',endpoint='my_list',view_func=ListView.as_view("list"))
class ADSView(views.View): def __init__(self): super(ADSView, self).__init__() self.context = { 'ads': '这里是wqbin的博客' } class LoginView(ADSView): def dispatch_request(self): self.context.update({ 'username': 'wqbin' }) return render_template('login.html',**self.context) class RegistView(ADSView): def dispatch_request(self): return render_template('regist.html',**self.context) app.add_url_rule('/login/',view_func=LoginView.as_view('login')) app.add_url_rule('/regist/',view_func=RegistView.as_view('regist'))
基于调度方法的类视图
基于请求方法的类视图:
基于方法的类视图,是根据请求的method来执行不同的方法的。
如果用户是发送的get请求,那么将会执行这个类的get方法。如果用户发送的是post请求,那么将会执行这个类的post方法。其他的method类似,比如delete、put。
这种方式,可以让代码更加简洁。
所有和get请求相关的代码都放在get方法中,所有和post请求相关的代码都放在post方法中。就不需要跟之前的函数一样,通过request.method == 'GET'。
class LoginView(views.MethodView): def __render(self,error=None): return render_template('login.html',error=error) def get(self): return self.__render() def post(self): username = request.form.get('username') password = request.form.get('password') if username == 'wqbin' and password == '123456': return '登录成功' else: return self.__render(error='用户名或密码错误') app.add_url_rule('/login/',view_func=LoginView.as_view('login'))
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="" method="post"> <table> <tbody> <tr> <td>用户名:</td> <td><input type="text" name="username"></td> </tr> <tr> <td>密码:</td> <td><input type="password" name="password"></td> </tr> <tr> <td></td> <td><input type="submit" value="立即登录"></td> </tr> </tbody> </table> {% if error %} <p style="color: red;">{{ error }}</p> {% endif %} </form> </body> </html>
类视图中使用装饰器
- 如果使用的是函数视图,那么自定义的装饰器必须放在`app.route`下面
- 类视图的装饰器,需要重写类视图的一个类属性`decorators`,这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰器。
def login_required(func): @wraps(func) def wrapper(*args,**kwargs): username = request.args.get('username') if username and username == 'wqbin': return func(*args,**kwargs) else: return '请先登录' return wrapper @app.route('/settings/') @login_required def settings(): return '这是设置界面' class ProfileView(views.View): decorators = [login_required] def dispatch_request(self): return '这是个人中心界面' app.add_url_rule('/profile/',view_func=ProfileView.as_view('profile'))
蓝图的基本使用
1. 蓝图的作用
让我们的Flask项目更加模块化,结构更加清晰。
可以将相同模块的视图函数放在同一个蓝图下,同一个文件中,方便管理。
2. 基本语法:
* 在蓝图文件中导入Blueprint:
from flask import Blueprint user_bp = Blueprint('user',__name__)
我们仔细了解Blueprint类的构造函数:
def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, root_path=None): _PackageBoundObject.__init__(self, import_name, template_folder, root_path=root_path) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain self.static_folder = static_folder self.static_url_path = static_url_path self.deferred_functions = [] if url_defaults is None: url_defaults = {} self.url_values_defaults = url_defau
* 在主app文件中注册蓝图:
from blueprints.user import user_bp app.regist_blueprint(user_bp)
3. 如果想要某个蓝图下的所有url都有一个url前缀,那么可以在定义蓝图的时候,指定url_prefix参数:
user_bp = Blueprint('user',__name__,url_prefix='/user/')
在定义url_prefix的时候,要注意后面的斜杠,如果给了,那么以后在定义url与视图函数的时候,就不要再在url前面加斜杠了。
4. 蓝图模版文件的查找:
* 如果项目中的templates文件夹中有相应的模版文件,就直接使用了。
* 如果项目中的templates文件夹中没有相应的模版文件,那么就到在定义蓝图的时候指定的路径中寻找。并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录。比如:
news_bp = Blueprint('news',__name__,url_prefix='/news',template_folder='bp_template')
因为这个蓝图文件是在blueprints/news.py,那么就会到blueprints这个文件夹下的bp_template文件夹中寻找模版文件。
5. 蓝图中静态文件的查找规则:
* 在模版文件中,加载静态文件,如果使用url_for('static'),那么就只会在app指定的静态文件夹目录下查找静态文件。
* 如果在加载静态文件的时候,指定的蓝图的名字,比如`news.static`,那么就会到这个蓝图指定的static_folder下查找静态文件。
<link rel="stylesheet" href="{{ url_for('static',filename='news_list.css') }}"> <link rel="stylesheet" href="{{ url_for('newsbp.static',filename='news_list.css') }}">
6. url_for反转蓝图中的视图函数为url:
* 如果使用蓝图,那么以后想要使用url_for反转蓝图中的视图函数为url,那么就应该在使用url_for的时候指定这个蓝图,比如`newsbp.news_list`。否则就找不到这个endpoint。在模版中的url_for同样也是要满足这个条件,就是指定蓝图的名字。
* 即使在同一个蓝图中反转视图函数,也要指定蓝图的名字。
@app.route('/') def hello_world(): print(url_for('newsbp.news_list')) return render_template('index.html')
子域名实现详解
蓝图实现子域名:
1. 使用蓝图技术。
2. 在创建蓝图对象的时候,需要传递一个subdomain参数,来指定这个子域名的前缀。例如:cms_bp = Blueprint('cmsbp',__name__,subdomain='cms')。
from flask import Blueprint cms_bp = Blueprint('cmsbp',__name__,subdomain='cms') @cms_bp.route('/') def index(): return 'cms index page'
3. 需要在主app文件中,需要配置app.config的SERVER_NAME参数。例如:
app.config['SERVER_NAME'] = 'jd.com:5000'
- ip地址不能有子域名。
- localhost也不能有子域名。
4. 在C:WindowsSystem32driversetc下,找到hosts文件,然后添加域名与本机的映射。例如:
127.0.0.1 jd.com
127.0.0.1 cms.jd.com
域名和子域名都需要做映射。