**转义字符**
**其它过滤器**
>
> **小提示**
>
>
> jinja2模板 默认全局开启了自动转义功能
>
>
> * safe 过滤器:可以关闭一个字符串的自动转义
> * escape 过滤器:对某一个字符串进行转义
> * autoescape 标签,可以对他包含的代码块关闭或开启自动转义
> + {% autoescape true/false %} 代码块 {% endautoescape %
>
>
>
**自定义过滤器**
只有当系统提供的过滤器不符合需求后,才须自定义过滤器 过滤器本质上就是一个函数。 如果在模版中调用这个过滤器,那么就会将这个变量的值作为第一 个参数传给过滤器这个函数, 然后函数的返回值会作为这个过滤器的返回值。 需要使用到一个装饰器: @app.template\_filter(‘过滤器名称’)
**自定义数据替换过滤器**
例如:将新闻中出现的 所有“ 我是九你是三,除了你还是你” 换成 “你不用多好,我喜欢就好”
#将模版设置为自动加载模式
app.config[‘TEMPLATES_AUTO_RELOAD’]=True
@app.template_filter(‘cut’)
def cut(value):
value=value.replace(“我是九你是三,除了你还是你”,‘你不用多好,我喜欢就好’)
return value
使用自定义过滤器:{ {新闻内容值|cut}}
**自定义时间过滤器**
例如:操作发布新闻 与现在的时间间隔
from datetime import datetime
#需求:操作发布新闻 与现在的时间间隔
@app.template_filter(‘handle_time’)
def handle_time(time):
“”"
time距离现在的时间间隔
1. 如果时间间隔小于1分钟以内,那么就显示“刚刚”
2. 如果是大于1分钟小于1小时,那么就显示“xx分钟前”
3. 如果是大于1小时小于24小时,那么就显示“xx小时前”
4. 如果是大于24小时小于30天以内,那么就显示“xx天前”
5. 否则就是显示具体的时间 2030/10/2016:15
“”"
if isinstance(time, datetime):
now = datetime.now()
timestamp = (now - time).total_seconds()
if timestamp < 60:
return “刚刚”
elif timestamp >= 60 and timestamp < 60 * 60:
minutes = timestamp / 60
return “%s分钟前” % int(minutes)
elif timestamp >= 60 * 60 and timestamp < 60 * 60 * 24:
hours = timestamp / (60 * 60)
return ‘%s小时前’ % int(hours)
elif timestamp >= 60 * 60 * 24 and timestamp < 60 * 60 * 24 * 30:
days = timestamp / (60 * 60 * 24)
return “%s天前” % int(days)
else:
return time.strftime(‘%Y/%m/%d %H:%M’)
else:
return time
发布时间:{ {新闻创建时间|handle_time}}
### 流程控制-选择结构

所有的控制语句都是放在 {% … %} 中,并且有一个语句 {% endxxx %} 来进 行结束!
\*\*if:\*\*if语句和python中的类似,可以使用 >,=,==,!= 来进行判 断,也可以通过 and,or,not,() 来进行逻辑合并操作
{% if age >= 18 %}
{ { age }}岁,成年人,可以通宵打游戏
{ { age }}岁,未成年人,可以通宵学习
>
> **注意**:
>
>
> if 条件判断语句必须放在 {% if statement %} 中间,并且还必须有结束 的标签 {% endif %} 。
>
>
>
### 流程控制-循环结构

for…in… for循环可以遍历任何一个序列包括列表、字典、元组。并且 可以进行反向遍历,以下将用几个例子进行解释:
**列表**
**遍历字典**
{% for key in person.keys() %} { { key}} {% endfor %} {% for val in person.values() %} { { val}} {% endfor %} {% for item in person.items() %} { { item}} {% endfor %} {% for key,value in person.items() %} { { value}} {% endfor %}
如果序列中没有值的时候,进入else 反向遍历用过滤器 reverse:
并且Jinja中的for循环还包含以下变量,可以用来获取当前的遍历状 态:

>
> **总结**
>
>
> 在 jinja2 中的 for 循环,跟 python 中的 for 循环基本上是一模一样的 也是 for…in… 的形式。并且也可以遍历所有的序列以及迭代器 唯一不同的是, jinja2 中的 for 循环没有 break 和 continue 语句
>
>
>
### 导入模板include

1. 这个标签相当于是直接将指定的模版中的代码复制粘贴到当前位 置。
2. include 标签,如果想要使用父模版中的变量,直接用就可以了,不 需要使用 with context 。
3. include 的路径,也是跟 import 一样,直接从 templates 根目录下去找, 不要以相对路径去找。
SXT {% include "common/head.html" %}
### set与with标签

**set的使用**
在模版中,可以使用 set 语句来定义变量
{% set uname=‘sxt’%}
用户名:{ { uname }}
一旦定义了这个变量,那么在后面的代码中,都可以使用这个变 量,就类似于Python的变量定义是一样的
**with语句**
with 语句定义的变量,只能在 with 语句块中使用,超过了这个代码 块,就不能再使用了
{% with classroom=‘python202’%}
班级:{ { classroom }}
>
> **注意**
>
>
> 关于定义的变量, with 语句也不一定要跟一个变量, 可以定义一个空的 with 语句, 需要在指定的区域才能使用的情况,可以set与with组合使用。
>
>
>
{% with %}
{% set pname=‘李思思’ %}
娱乐县县长:{ { pname }}
{% endwith %}
### 静态文件

**静态文件**:css文件 js文件 图片文件等文件
加载静态文件使用的是 url\_for 函数。然后第一个参数需要为 static ,第 二个参数需要为一个关键字参数 filename=‘路径’ 。
**语法**
{ { url_for(“static”,filename=‘xxx’) }}

>
> **注意** 路径查找,要以当前项目的 static 目录作为根目录
>
>
>
### 模板继承

**为什么需要模版继承**
模版继承可以把一些**公用的代码**单独抽取出来放到一个父模板中 以后**子模板直接继承**就可以使用了。 这样可以**重复的利用代码**,并且以后**修改起来**也比较方便

**模版继承语法**
使用 extends 语句,来指明继承的父模板。父模板的路径,也是相对 于 templates 文件夹下的绝对路径
{% extends “base.html” %}
**block语法**
一般在父模版中,定义一些公共的代码。子模板可能要根据具体的 需求实现不同的代码。这时候父模版就应该有能力提供一个接口,让子模板来实现。从而 实现具体业务需求的功能。
**父模板**
{% block block的名字 %}
{% endblock %}
**子模板**
{% block block的名字 %}
子模板中的代码
{% endblock %}
**调用父模版代码block中的代码**
默认情况下,子模板如果实现了父模版定义的block。那么子模板 block中的代码就会覆盖掉父模板中的代码。 如果想要在子模板中仍然保持父模板中的代码,那么可以使用 {
{ super( ) }} 来实现
**父模板**
{% block block_body %}
我是父模版block_body处的内容
**子模板**
{% block block_body%}
{ { super() }}
我是子模版block_body处的内容
**调用另外一个block中的代码**
如果想要在另外一个模版中使用其他模版中的代码。那么可以通过 {
{ self.其他block名字() }} 就可以了
{% block title %}
sxt首页
{% endblock %}
{% block block_body%}
{ { self.title() }}
我是子模版block_body处的内容
>
> **注意**
>
>
> 1. 子模板中的代码,第一行,应该是 extends
>
>
> 2. 子模板中,如果要实现自己的代码,应该放到block中。如果 放到其他地方,那么就不会被渲染
>
>
>
## Flask视图
### **add\_url\_rule与app.route**
**add\_url\_rule**
add\_url\_rule(rule,endpoint=None,view\_func=None)
这个方法用来添加url与视图函数的映射。 如果没有填写 endpoint ,那么默认会使用 view\_func 的名字作为 endpoint 。 以后在使用 url\_for 的时候,就要看在映射的时候有没有传递 endpoint 参 数,如果传递了,那么就应该使用 endpoint 指定的字符串, 如果没有传递,那么就应该使用 view\_func 的名字。
def my_list():
return “我是列表页”
app.add_url_rule(‘/list/’,endpoint=‘sxt’,view_func=my_list)
**app.route原理剖析**
这个装饰器底层,其实也是使用 add\_url\_rule 来实现url与视图函数映射 的。
from flask import Flask,url_for
app = Flask(name)
@app.route(‘/’,endpoint=‘index’)
def index():
print(url_for(‘show’))
print(url_for(‘index’))
return “Hello”
def show_me():
return “这个介绍信息!!”
app.add_url_rule(‘/show_me’,view_func=show_me,endpoint=‘show’)
if name ==‘main’:
app.run(debug=True)
### 类视图

之前我们接触的视图都是函数,所以一般简称函数视图。 其实视图也可以基于类来实现,类视图的好处是支持继承, 但是类视图不能跟函数视图一样,写完类视图还需要通过 app.add\_url\_rule(url\_rule,view\_func) 来进行注册
**标准类视图使用步骤**
1. 标准类视图,必须继承自 flask.views.View
2. 必须实现 dispatch\_request 方法,以后请求过来后,都会执行这个方 法。 这个方法的返回值就相当于是之前的视图函数一样。也必须返回 Response 或者子类的对象,或者是字符串,或者是元组。
3. 必须通过 app.add\_url\_rule(rule,endpoint,view\_func) 来做url与视图的映射。 view\_func 这个参数,需要使用类视图下的 as\_view 类方法类转换: ListView.as\_view(‘list’) 。
4. 如果指定了 endpoint ,那么在使用 url\_for 反转的时候就必须使用 endpoint 指定的那个值。如果没有指定 endpoint ,那么就可以使用 as\_view(视图名字) 中指定的视图名字来作为反转。
from flask import Flask,url_for
from flask.views import View
app= Flask(name)
@app.route(‘/’)
def index():
# print(url_for(‘mylist’))
print(url_for(‘my’))
return ‘Hello’
class ListView(View):
def dispatch_request(self):
return ‘返回了一个List的内容!!’
app.add_url_rule(‘/list’,endpoint=‘my’,view_func=ListView.as_view(‘mylist’))
with app.test_request_context():
print(url_for(‘my’))
if name ==‘main’:
app.run(debug=True)
**类视图的好处**
1.可以继承,把一些共性的东西抽取出来放到父视图中,子视图直 接拿来用就可以了。
2.但是也不是说所有的视图都要使用类视图,这个要根据情况而 定。视图函数用得最多。
from flask import Flask,jsonify
from flask.views import View
app = Flask(name)
class BaseView(View):
def get_data(self):
raise NotImplementedError
def dispatch_request(self):
return jsonify(self.get_data())
class JsonView(BaseView):
def get_data(self):
return {‘uname’:‘吕布’,‘age’:20}
class Json2View(BaseView):
def get_data(self):
return [
{‘name’:‘zs’,‘lua’:‘Python’},
{‘name’:‘lisi’,‘lua’:‘Python’},
]
app.add_url_rule(‘/base’,view_func=BaseView.as_view(‘base’))
app.add_url_rule(‘/json’,view_func=JsonView.as_view(‘json’))
app.add_url_rule(‘/json2’,view_func=Json2View.as_view(‘json2’))
if name ==‘main’:
app.run(debug=True)
### 基于调度方法的类视图

1. 基于方法的类视图,是根据请求的 method 来执行不同的方法的。 如果用户是发送的 get 请求,那么将会执行这个类的 get 方法。 如果用户发送的是 post 请求,那么将会执行这个类的 post 方法。其他 的method类似,比如 delete 、 put
2. 这种方式,可以让代码更加简洁。所有和 get 请求相关的代码都放 在 get 方法中,所有和 post 请求相关的代码都放在 post 方法中。就不 需要跟之前的函数一样,通过 request.method == ‘GET’
class LoginView(views.MethodView):
def get(self,error=None):
return
render_template(‘login.html’,error=error)
def post(self):
#模拟实现
#拿到前端页面传过来的 账号 和密码 去数据库做查询操作 查询到 (跳转主页面) ,反之跳转到login.html页面并给出错误提示信息
uname = request.form[‘uname’]
pwd = request.form[‘pwd’]
if uname==“sxt” and pwd ==“123”:
return render_template(‘index.html’)
else:
return self.get(error=“用户名或者密码错误”)
app.add_url_rule(‘/login/’,view_func=LoginVi
ew.as_view(‘my_login’))
HTML
账号: | |
密码: | |
{# { { error }}#} {# 优化写法 :判断 #} {% if error %} { { error }} {% endif %} |
### 装饰器

简言之,python装饰器就是用于拓展**原来函数功能**的一种函数,这 个**函数的特殊之处在于它的返回值也是一个函数**, 使用python装饰器的**好处**就是在不用更改原函数的代码前提下给函 数增加新的功能。
1. 在视图函数中使用自定义装饰器,那么自己定义的装饰器必须放 在 app.route 下面。 否则这个装饰器就起不到任何作用。
**案例1**
需求:查看设置个人信息时,只有检测到用户已经登录了才能查 看,若没有登录,则无法查看并给出提示信息
定义装饰器
def login_required(func):
@wraps(func)
def wrapper(*arg,**kwargs):
uname = request.args.get(‘uname’)
pwd = request.args.get(‘pwd’)
if uname == ‘zs’ and pwd == ‘123’:
logging.info(f’{uname}:登录成功’)
return func(*arg,**kwargs)
else:
logging.info(f’{uname}:尝试登录,但没成功’)
return ‘请先登录’
return wrapper
使用装饰器
@app.route(‘/settings/’)
@login_requierd
def settings():
return ‘这是设置界面’
2. 在类视图中使用装饰器,需要重写类视图的一个类属性 decorators , 这个类属性是一个列表或者元组都可以,里面装的就是所有的装饰 器。
**案例2**
**需求**: 查看设置个人信息时,只有检测到用户已经登录了才能查看, 若没有登录,则无法查看并给出提示信息
使用装饰器
class ProfileView(views.View):
decorators = [login_requierd]
def dispatch_request(self):
return ‘这是个人中心界面’
app.add_url_rule(‘/profile/’,view_func=ProfileView.as_view(‘profile’))
### **蓝图介绍**
在Flask中,使用蓝图Blueprint来分模块组织管理。 蓝图实际可以理解为是存储一组视图方法的容器对象,其具有如下 特点:
* 一个应用可以具有多个Blueprint
* 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user” 、 “/goods”
* Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应 用的视图和函数的
* 在一个应用初始化时,就应该要注册需要使用的Blueprint
>
> **注意**
>
>
> Blueprint并不是一个完整的应用,它不能独立于应用运行,而 必须要注册到某一个应用中
>
>
>
**使用方式**
使用蓝图可以分为三个步骤
* 1 创建一个蓝图对象
user_bp=Blueprint(‘user’,name)
* 在这个蓝图对象上,
@user_bp.route(‘/’)
def user_profile():
return ‘user_profile’
* 在应用对象上注册这个蓝图对象
app.register_blueprint(user_bp)
**指定蓝图的url前缀**
在应用中注册蓝图时使用 url\_prefix 参数指定
app.register_blueprint(user_bp,url_prefix=‘/user’)
app.register_blueprint(goods_bp,url_prefix=‘/goods’)
### 蓝图的目录结构
为了让项目代码更加清晰,可以通过将代码分在不同的文件里进行 管理
**根据功能模块**
对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到 Python包的 \_\_init\_\_.py 文件中
--------- project # 工程目录
|------ main.py # 启动文件
|------ user #用户蓝图
| |— init.py # 此处创建蓝图对象
| |— view.py
| |— …
|------ goods # 商品蓝图
| |— init.py
| |— …
|…
**根据技术模块**
--------- project # 工程目录
|------ main.py # 启动文件
|------ view #用户蓝图
| |— user.py # 此处创建蓝图对象
| |— item.py
| |— view.py
| |— …
|…
### 蓝图中模版文件

**寻找规则**
* 如果项目中的templates文件夹中有相应的模版文件,就直接使 用了。
* 如果项目中的templates文件夹中没有相应的模版文件,那么就 到在定义蓝图的时候指定的路径中寻找。
+ 并且蓝图中指定的路径可以为相对路径,相对的是当前这个蓝图文件所在的目录

因为这个蓝图文件是在user/view.py,那么就会到blueprints这个 文件夹下的user\_page文件夹中寻找模版文件。
**小总结:** 常规:蓝图文件在查找模版文件时,会以templates为根目录进行 查找
>
> **注意**
>
>
> * 1 个性化coder喜欢在【创建蓝图对象的时候】 指定 模版文 件的查找路径,如下 news\_bp =Blueprint(‘news’,\_\_name\_\_,url\_prefix=‘/news’,template\_folder=‘news\_page’)
> * 2 只有确定templates目录下没有对应的 html文件名的时候, 才会去蓝图文件指定的目录下查找,指定才会生效
> * 3 若templates目录下,有一个与蓝图文件指定的目录下同名 的一个 html文件时,优先走templates目录下的东西
>
>
>
### **蓝图中静态文件**

蓝图内部静态文件 蓝图对象创建时不会默认注册静态目录的路由。需要我们在创建时 指定 static\_folder 参数。 下面的示例将蓝图所在目录下的 static\_admin 目录设置为静态目录:
user=Blueprint(“user”,name,static_folder=‘user_static’)
app.register_blueprint(admin,url_prefix=‘/user’)
也可通过 static\_url\_path 改变访问路径
user =Blueprint(‘user’,name,template_folder=‘user_page’,static_folder=‘user_static’,static_u
rl_path=‘/static’)
app.register_blueprint(user,url_prefix=‘/user’)
>
> **总结** 【掌握】查找方式1:查找静态文件时,正常情况下,会以 static为根目录进行查找 【了解】查找方式2:查找静态文件时,非正常情况下,需要用 url\_for(‘蓝图的名字.static’),然后会去蓝图对象在创建时指定的 静态文件夹目录下 去查找静态文件
>
>
>
### 蓝图url\_for函数

如果使用蓝图,那么以后想要反转蓝图中的视图函数为url,就应该 在使用url\_for的时候指定这个蓝图名字。 app类中、模版中、同一个蓝图类中都是如此。否则就找不到这个 endpoint
html文件中
新闻列表 OK写法
{# 新闻列表 no Ok写法#}
python文件中
from flask import
Blueprint,render_template,url_for
user_bp=Blueprint(‘news’,name,url_prefix=‘/user’,template_folder=‘user_page’,static_folder=‘user_static’)
@user_bp.route(‘/list/’)
def user_list():
#如下写法:才找得到 url_for(‘蓝图名称.方法名’)
print(url_for(‘user.user_list’)) #/user/list/
print(url_for(‘user.user_detail’)) #/user/detail/
return render_template(‘user_list.html’)
@user_bp.route(‘/detail/’)
def user_detail():
return ‘用户详情页面’
### 子域名实现
蓝图实现子域名:
1. 使用蓝图技术。
2. 在创建蓝图对象的时候,需要传递一个 subdomain 参数,来指定这 个子域名的前缀。
cms_bp=Blueprint(‘cms’,name,subdomain=‘cms’)
3. 需要在主app文件中,需要配置app.config的SERVER\_NAME参 数。例如:
app.config[‘SERVER_NAME’]=‘baidu.com:5000’
4. 在windows: C:\Windows\System32\drivers\etc 下,找到hosts文件,然后添 加域名与本机的映射。Linux: /etc/hosts 域名和子域名都需要做映射
>
> **注意** ip地址不能有子域名 localhost也不能有子域名
>
>
>
## Flask高级
### Flask设置Cookie

**设置**
设置cookie是在Response的对象上设置。 flask.Response 对象有一个 set\_cookie 方法,可以通过这个方法来设置 cookie 信息。
key,value形式设置信息
from flask import Flask, make_response
app = Flask(name)
@app.route(‘/cookie’)
def set_cookie():
resp = make_response(‘set cookie ok’)
resp.set_cookie(‘uname’, ‘itbaizhan’)
return resp
**查看Cookie**
在Chrome浏览器中查看cookie的方式:
方式1:借助于 开发调式工具进行查看
方式2:在Chrome的设置界面->高级设置->内容设置->所有 cookie->找到当前域名下的cookie。
from flask import request
@app.route(‘/get_cookie’)
def get_cookie():
resp = request.cookies.get(‘uname’)
return resp
**删除cookie**
方式1:通过 Response对象.delete\_cookie ,指定cookie的key,就可以删 除cookie了。
from flask import request
@app.route(‘/delete_cookie’)
def delete_cookie():
response = make_response(‘helloworld’)
response.delete_cookie(‘uname’)
return response
方式2:在客户端浏览器人为的删除(清除浏览器浏览历史记录 后,很多网站之前免密登录的都不好使了)
**Cookie的有效期**
**默认的过期时间**:如果没有显示的指定过期时间,那么这个cookie 将会在浏览器关闭后过期。 **max\_age**:以秒为单位,距离现在多少秒后cookie会过期。
**expires**:为datetime类型。这个时间需要设置为格林尼治时间, 相对北京时间来说 会自动+8小时 如果max\_age和expires都设置了,那么这时候以max\_age为标 准。
>
> **注意**
>
>
> max\_age在IE8以下的浏览器是不支持的。 expires虽然在新版的HTTP协议中是被废弃了,但是到目前为 止,所有的浏览器都还是能够支持,所以如果想要兼容IE8以下 的浏览器,那么应该使用expires,否则可以使用max\_age。
>
>
>
from flask import Flask,Response
app = Flask(name)
@app.route(‘/’)
def index():
return ‘Hello!!’
@app.route(‘/create_cookie/defualt/’)
def create_cookie1():
resp = Response(‘通过默认值,设置cookie有效期’)
resp.set_cookie(‘una