Flask 路由、视图、模版、闪现详解
一、注册路由
1、定义
路由:指根据url定位到具体的类或者函数的程序,本质就是建立url跟程序之间的映射。flask中使用的路由被称之为注册路由
2、路由传参
2.1 动态传参
# 动态传参语法 @app.route(路径+/<参数名>/') #例子 @app.route('/index/<id>/') def index(id) print(id) return id
使用转换器进行对传参的类型进行限定
string | 默认的数据类型,接收没有任何斜杆\ /的字符串 |
int | 整型 |
float | 浮点型 |
path | 跟string类型类似,它可以接受斜杆/\ |
uuid | uuid格式的字符串 |
any | 可以指定多种路径 |
#使用方法 @app.route('/index/<int:id>/') def index(id): return id # any类型的用法、它可以指定多种路径 @app.route('/<any(user,admin):who>/<id>/') def home(who,id): if who== 'user': return '这里是普通用户{}的主页'.format(id) else: return '这里是管理员{}的主页'.format(id)
动态路由一般可以用来提高网站的曝光率
2.2 查询字符串传参(也就是?传参)
注意:request.args获取的是解析后的字典类型,request.query_string获取的是问号后的原生字符串。
http://127.0.0.1:5000/?name='hello'&age=18 @app.route('/') def home(): print(request.args) #>: ImmutableMultiDict([('name', "'hello'"), ('age', '18')]) print(request.query_string) #>: b'name=%27hello%27&age=18' return '这是跟路径'
3、自定义动态路由过滤(转换器)
3.1 正则匹配
from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter # 在路由中使用 @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
注意:必须继承werkzeug.routing 的BaseConverter类,regex属性指定路由过滤规则,app.url_map.converters本质就是一个字典
3.2 自定义动态路由过滤器处理动态路由
# -*-coding:utf-8 -*- from flask import Flask,url_for from werkzeug.routing import BaseConverter app = Flask(__name__) class ListConverter(BaseConverter): regex = '.*' def to_python(self, value): ''' 对动态参数进行处理 :param value: 路由里的动态参数 :return: 处理好的动态参数结果 ''' print(value) # a+b+c return value.split('+') def to_url(self, value): ''' 搭配url_for使用,对value进行处理 :param value: url_for指定的动态参数 :return: ''' print('to_url:',value) #结果:to_url: ['a', 'b'] return '+'.join(value) app.url_map.converters['list']=ListConverter @app.route('/test/<list:ids>/') #注意这里没有设置endpoint,所以默认是home def home(ids): # 对http://127.0.0.1:5000/test/a+b+c/发起请求 print('home:',ids) # 结果 ['a', 'b', 'c'] print(url_for('home',ids=['a','b'])) #结果 /test/a+b/ return 'home' if __name__ == "__main__": app.run(debug=True)
总结:to_python()方法是对动态路由的参数进行处理,to_url是对url_for()函数传入的动态参数进行处理
4、endpoint参数
4.1 endpoint 简介
endpoint是注册路由中的一个参数,也叫端点(类似函数的别名)通过url_for函数+endpoint参数值,可以反解出url
@app.route('/index11',endpoint='i') def index(): print(url_for('i')) #打印结果:/index11
4.2、对endpoint参数有关源码分析
def route(self, rule, **options): def decorator(f): # 1、先从options中获取endpoint值,如果没有,则赋值None给endpoint endpoint = options.pop("endpoint", None) # 2、将endpoint传到add_url_rule函数(也是注册路由的关键函数)中处理 self.add_url_rule(rule, endpoint, f, **options) return f return decorator def add_url_rule(self,rule,endpoint=None,view_func=None, provide_automatic_options=None, **options ): # 3、先判断endpoint是否为None if endpoint is None: # 4、endpoint值为None的时候,调用_endpoint_from_view_func endpoint = _endpoint_from_view_func(view_func) #4/7、 endpoint有值的时候,把它传给options options["endpoint"] = endpoint def _endpoint_from_view_func(view_func): # 5、用断言来判断函数是否为None,是则报错 assert view_func is not None, "expected view func if endpoint is not provided." # 6、返回一个函数的的函数名 return view_func.__name__
5、add_url_rule(): 注册路由的第二种方式
def index(): return '注册路由的另一种方式' app.add_url_rule('/index/',endpoint('自定义名字'),view_func=index(函数地址)
6、注册路由中用到的参数
# ----------常用参数-------------- # @app.route()和app.add_url_rule参数 rule #路由路径 view_func # 视图函数名称 endpoint # 端点名字,用于url_for反向解析url methods # 允许请求的方式 # -----------其他参数--------------- # 1、对url是否加/是否有严格要求 strict_slashes=Flase #访问http://127.0.0.1/index和http://127.0.0.1/index/都可以 @app.route('/index', strict_slashes=True) #仅可以访问http://www.xx.com/index # 2、重定向 redirect_to @app.route("/index",redirect_to='/home/') #当你访问index页面的时候,会跳到home页面去 # 3、为函数提供默认参数值 defaults = None #默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'} @app.route('/home',defaults={'k':'v'}) def home(k): print(k) # v # 4、设置子域名 subdomain from flask import Flask,url_for app = Flask(__name__) app.debug = True ''' 先在hosts设置域名解析(就是在本机的hosts文件上编辑上域名对应ip的关系) 域名解析会先解析本地如果没有再解析dns服务器 C:\Windows\System32\drivers\etc\hosts 127.0.0.1 mark.com 127.0.0.1 admin.mark.com ''' app.config['SERVER_NAME'] = 'mark.com:5000' # 这个代表访问这个域名的时候要访问5000端口 @app.route("/") def index(): return '设置域名成功' @app.route("/admin_demo/",subdomain='admin') def admin_demo(): return '设置子域名成功' ''' 在浏览器中访问主域名 mark.com:5000/ 在浏览器中访问子域名 admin.mark.com:5000/admin_demo/ 注意:后面跟的path路径部分正常写 ''' if __name__ == '__main__': app.run(host='127.0.0.1',port=5000) # 测试服务器不稳定,尽量手动制定ip和端口
二、视图
1、视图函数
视图函数跟django的视图函数类似,只不过在视图函数中加装饰器有点不一样,flask中需要将装饰器卸载注册路由下面,视图函数上面,同时,装饰器内部要使用@wraps(func)方法,用于保护被装饰函数的属性
from flask import Flask, request from functools import wraps app = Flask(__name__) def login_verify(func): @wraps(func) def wrapper(*args, **kwargs): user_name = request.args.get('user') password = request.args.get('password') if user_name == 'mark' and password == '123': return func(*args,**kwargs) else: return '请登录' return wrapper @app.route('/my_info/') @login_verify def my_info(): return '个人信息页面'
2、视图类
2.1 标准类视图类
''' 1、继承flask.views.View 2、注册路由:app.add_url_rule(rule,endpoint,view_func) --rule:路由地址 --endpoint: 端点 --view_func: 函数名称(不用加括号) 3、重写dispatch_request方法 ''' # -*-coding:utf-8 -*- from flask import Flask,views,url_for app=Flask(__name__) class S_Test(views.View): def dispatch_request(self): print(url_for('List')) #/stest return '这是标准视图类' ''' 注意:如果设置了endpoint,那么使用url_for反转的时候,就要用endpoint的值 如果没有,则可以直接用as_view中的指定的视图名字来作为反转 as_view('视图名字') ''' app.add_url_rule('/stest',view_func=S_Test.as_view('List')) if __name__=='__main__': app.run(debug=True)
2.2 基于调度方法的视图类
''' 1、继承flask.views.MethodView 2、指定支持请求类型 methods=[] 3、指定使用哪些装饰器 decorators = [] 4、写相应的请求函数 5、注册路由 ''' from flask import Flask,views,url_for from functools import wraps app=Flask(__name__) class Test(views.MethodView): methods = ['GET',"POST"] #设置可以接收的请求方法 decorators = [auth] # 指定装饰器,注意不能添加单引号或者双引号 # get请求方法 def get(self): return '这是get请求方法' # post请求方法 def post(self): return '这是post请求方法' # 同样,如果没有指定endpoint,url_for反转的时候,同样是使用as_view中的name值 app.add_url_rule('/test',view_func=Test.as_view(name='test1')) if __name__=='__main__': app.run(debug=True)
三、模板引擎
1、概述
模版引擎:指实现视图的业务逻辑和返回给前端的页面逻辑分离的工具。
作用:实现业务逻辑和页面逻辑的分离,实现动态的去渲染页面
模版路径配置,可以在Flask类的template_folder参数设置
app = Flask(__name__,template_folder='模板位置') app =Flask(__name__) app.template_folder='模版位置'
2、模版传参
# 第一种,在render_template中把参数传过去 @app.route('/') def index(): return render_template('index.html',name='hello',age=18) # 第二种,把参数放在字典中,将字典传递过去 @app.route('/') def index(): context_dict={ 'name':'hello', 'age':18 } return render_template('index.html',context=context_dict) # 第三种,将字典打散传递过去 return render_template('index.html',**context_dict) # ------------前端对接收到参数的处理-------------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> # 参数关键词 {{ name }} # 字典参数 {{ context.name }} {{ context.get('age') }} {{context['name']}} </body> </html>
3、模板引擎使用url_for
注意:url_for在模板中用法跟在视图函数的用法一样,没有指定endpoint,就使用函数名,指定了,就使用endpoint的值进行反转
# 例子 @app.route('/index') def index(): return render_template('index.html') @app.route('/home',endpoint='h') def home(): return render_template('index.html') # html文件 <a href="{{ url_for('h') }}">home</a> <a href="{{ url_for('index') }}">index</a> #这句会报错,因为home函数的路由已经设置了endpoint <a href="{{ url_for('home') }}">home1</a>
4、jinja2控制语句使用
# 控制语句都是以{% ... %} 开始 ,以{ %end...% }结束
4.1 逻辑语句和if 语句
''' 判断: >、<、<=、==、!= 逻辑:and、or、not、() ''' { % if age>18 %} ... {% elif age ==9 %} ... {% else %} ... {%endif %} #表示结束判读
4.2 循环语句和for循环
# 遍历列表,可迭代对象,字典等 {% for dict in d %} ... {% else %} ... # 遍历没有内容,才会执行else,for else逻辑在python是没有的 {% endfor %} # 反转遍历 {% for dict in d | reverse %} #------------ for 常用循环变量-------------- loop.index #当前循环的索引(从1开始) loop.index() #当前循环的索引(从0开始) loop.first #是否是第一次循环,是返回True,否则返回Flase loop.last #是否是最后一次循环,是返回True,否则返回Flase loop.length #总共可以循环的次数 / 迭代器的长度 # 例子 {% for college in colleges %} {% if loop.first %}
<tr style="background: blue">
{% elif loop.last %} <tr style="background: yellow "> {% else %} <tr> {% endif %} <td>{{ loop.index }}</td> <td>{{ loop.index0 }}</td> <td>{{ college.name }}</td> <td>{{ loop.length }}</td> </tr> {% endfor %}
5、静态文件在模板中的使用
{{ url_for('static',filename='相对于static文件夹的路径') }}
<!DOCTYPE html> <html lang="en"> <head> ... <script src="{{ url_for('static',filename='js/demo.js') }}"></script> </head> ....
6、模板继承
定义一个基模版,在基模版中定义一些block块,以便给子模版实现自己的功能
# 1、基模版parent.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> # top是自己定义的变量名 {% block top %} <p>我是父模版</p> {% endblock %} </body> </html> # 2、子模板son.html继承parent.html {% extends 'parent.html' %} {% block top %} {{ super() }} #表示继承父模板中的本块的代码 <p>我继承了parent模版</p> {% endblock %}
四、闪现
1 定义
闪现:flask提供的一个简单的方法,用来向用户反馈信息,本质是将信息放在session中,必须设置secret_key,下次请求的时候,再从session中获取。
注意:闪现有效期只有一次,当下一次请求获取闪现的信息后,它就失效了
2 用法
from flask import Flask,flash,make_response,get_flashed_messages # 需要导入flash、get_flashed_messages 用来设置和获取闪现值 app=Flask(__name__) app.secret_key='shanxian' @app.route('/') def home(): flash('欢迎来到首页') #设置闪现值 return 'index' @app.route('/index') def index(): a=get_flashed_messages() #获取闪现值 return 'index11'
前端获取闪现信息
{% with messages= get_flashed_messages() %} # 获取所有闪现信息,返回的是列表 {% for m in messages %} <li>{{ m }}</li> {% endfor %} {% endwith %}
3 分类闪现
默认分类是:message
#1、设置: flash('hello',category='test') # 或 flash('hello','test') # 2、取值,获取所有闪现信息的类别和内容 res=get_flashed_messages(with_categories=True) # ------------前端获取(注意with_categories=true别忘记设置)--------------- {% with messages= get_flashed_messages(with_categories=true) %} # 获取所有闪现信息,返回的是列表 {% for c,m in messages %} <li>{{ m }},{{ c }}</li> {% endfor %} {{ messages }} {% endwith %} ''' 结果如下:c是分类,m是闪现信息 test111,error test222,tes test333,test3 [('message', '欢迎来到首页'), ('error', 'test111'), ('tes', 'test222'), ('test3', 'test333')] '''
4 过滤闪现信息( category_filter参数)
#-----------后端视图函数过滤----------------- #默认category_filter =message get_flashed_messages(category_filter=['类名']) # -----------前端过滤闪现信息-------------- {% with messages= get_flashed_messages(category_filter=['test']) %} # 获取test类的闪现信息,返回的是列表 {% for m in messages %} <li>{{ m }}</li> {% endfor %} {% endwith %}