Jinja2 模版继承
Flask中的模版可以继承,通过继承可以把模版中许多重复出现的元素抽取出来,放在父模版中,并且父模版通过定义block给子模版。子模版根据需要,再实现这个block。
-
• 参考示例父模版 base.html
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="base.css" />
<title>{% block title %}{% endblock %}</title>
{% block head %}{% endblock %}
</head>
<body>
<div id="body">{% block body %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid/">you</a>
{% endblock %}
</div>
</body>
</html>
子模版
{% extends "base.html" %}
{% block title %}首页{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.detail{
color: red;
}
</style>
{% endblock %}
{% block body %}
<h1>这里是首页</h1>
<p class="detail">
首页的内容
</p>
{% endblock %}
首先第一行定义子模版继承父模版,并且可以看到子模版实现了父模版的title,并填充了自己的内容,再看head这个block,里面调用了super()这个函数,这个函数的目的是执行父模版中的代码,把父模版中的内容添加到子模版中,如果没有这一句,则父模版中处于head这个block中的代码将会被子模版中的代码给覆盖掉。
另外,模版中不能出现重名的block,如果一个地方需要用到另一个block中的内容,可以使用self.back name的方式进行引用。
<title>
{% block title %}
这是标题
{% endblock %}
</title>
<h1>{{ self.title() }}</h1>
以上示例中h1标签重用了title这个block中的内容,子模版实现了title这个block,h1标签也能拥有这个值。
另外,在子模版中,所有的文本标签和代码都要添加到父模版中继承的block中,否则这些文本和标签将不会被渲染。
Jinja2 转义
什么是转义? 在模版渲染字符串的时候,字符串有可能包含一些非常危险的字符比如<
、>
等,这些字符会破坏原来的html
标签结构,更严重的可能会发生XSS
跨域脚本攻击,因此如果碰到<
、>
这些字符的时候,应该转义成HTML
能正确表示这些字符的写法,比如>
在HTML
中应该用<
来表示等。
但是Flask
中默认没有开启全局自动转义,针对那些.html
, .htm
,.xml
, xhtml
结尾的文件,如果采用render_template
函数进行渲染的,则会开启自动转义。并且当用render_template_string
函数的时候,会将所有的字符串进行转义后再渲染。
而对于Jinja2
默认没有开启全局自动转义,原因如下:
-
• 渲染到模板中的字符串并不是所有都是危险的,大部分还是没有问题的,如果开启自动转义,那么将会带来大量的不必要的开销。
-
•
Jinja2
很难获取当前的字符串是否已经被转义过了,因此如果开启自动转义,将对一些已经被转义过的字符串发生二次转义,在渲染后会破坏原来的字符串。
在没有开启自动转义的模式下(比如以.conf
结尾的文件),对于一些不信任的字符串,可以通过{{ content_html|e }}
或者是{{ content_html|escape }}
的方式进行转义。在开启自动转义的模式下,如果想关闭自动转义,可以通过{{ content_html|safe }}
的方式关闭自动转义。
而{%autoescape true/false%}...{%endautoescape%}
可以将一段代码块放在中间,来关闭或开启自动转义。
{% autoescape false %}
<p>autoescaping is disabled here
<p>{{ will_not_be_escaped }}
{% endautoescape %}
Jinja2 数据类型和运算符
-
• 数据类型
Jinja2
支持许多数据类型,包括:字符串、整型、浮点型、列表、元组、字典、布尔。 -
• 运算符
-
•
+
号运算符:可以完成数字相加,字符串相加,列表相加。但是并不推荐使用+
运算符来操作字符串,字符串相加应该使用~
运算符。 -
•
-
号运算符:只能针对两个数字相减。 -
•
/
号运算符:对两个数进行相除。 -
•
%
号运算符:取余运算。 -
•
*
号运算符:乘号运算符,并且可以对字符进行相乘。 -
•
**
号运算符:次幂运算符,比如2**3=8。 -
•
in
操作符:跟python中的in
一样使用,比如{{1 in [1,2,3]}}
返回true
。 -
•
~
号运算符:拼接多个字符串,比如{{"Hello" ~ "World"}}
将返回HelloWorld
。
Jinja2 配置静态文件
web
应用中会出现大量的静态文件来使得网页更加生动美观。类似于CSS
式样文件,JavaScript
脚本文件,图片文件,字体文件等静态资源。在Jinja2
中加载静态文件非常简单,只需要通过url_for
全局函数就可以实现。
<link href="{{ url_for('static',filename='about.css') }}">
url_for
函数默认会在项目根目录下的static
文件夹中寻找about.css
文件,如果找到了,会生成一个相对于项目根目录下的/static/about.css
路径。也可以把静态文件不放在static
文件夹中,此时就需要具体指定。
app = Flask(__name__,static_folder='C:static')
那么访问静态文件的时候,将会到/static
这个文件夹下寻找。
Jinja2 高级视图
-
• 类视图视图可以基于类来实现,类视图的好处是支持继承,但是类视图不能跟函数视图一样,写完类视图还需要通过
app.add_url_rule(url_rule, view_func)
来进行注册。 -
• 标准类视图标准类视图是继承自
flask.views.View
,并且在子类中必须实现dispatch_request
方法,这个方法类似于视图函数,也要返回一个基于Response
或者其子类的对象。
from flask.views import View
class PersonalView(View):
def dispatch_request(self):
return "View"
# 类视图通过add_url_rule方法和url做映射
app.add_url_rule('/users/',view_func=PersonalView.as_view('personalview'))
-
• 基于调度方法的视图
Flask
还为我们提供了另外一种类视图flask.views.MethodView
,对每个HTTP方法执行不同的函数(映射到对应方法的小写的同名方法上)。
class LoginView(views.MethodView):
# 当客户端通过get方法进行访问的时候执行的函数
def get(self):
return render_template("login.html")
# 当客户端通过post方法进行访问的时候执行的函数
def post(self):
email = request.form.get("email")
password = request.form.get("password")
if email == 'xx@qq.com' and password == '111111':
return "登录成功!"
else:
return "用户名或密码错误!"
# 通过add_url_rule添加类视图和url的映射,并且在as_view方法中指定该url的名称,方便url_for函数调用
app.add_url_rule('/myuser/',view_func=LoginView.as_view('loginview'))
类视图中使用装饰器,比方有时候需要做权限验证的时候。
from flask import session
def login_required(func):
def wrapper(*args,**kwargs):
if not session.get("user_id"):
return 'auth failure'
return func(*args,**kwargs)
return wrapper
装饰器实装完成后,可以在类视图中定义一个属性叫做decorators
,然后存储装饰器。以后每次调用这个类视图的时候,就会执行这个装饰器。
class UserView(views.MethodView):
decorators = [user_required]
...
-
• 蓝图之前我们实装的
url
和视图函数都是处于同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以优雅的帮我们实现这种需求。
from flask import Blueprint
bp = Blueprint('user',__name__,url_prefix='/user/')
@bp.route('/')
def index():
return "用户首页"
@bp.route('profile/')
def profile():
return "个人简介"
在主程序中,通过app.register_blueprint()
方法将这个蓝图注册到url映射中。
from flask import Flask
import user # 导入蓝图
app = Flask(__name__)
app.register_blueprint(user.bp) # 注册蓝图
if __name__ == '__main__':
app.run(host='0.0.0.0',port=9000)
以后访问/user/
,/user/profile/
,都是执行的user.py
文件中的视图函数,这样就实现了项目的模块化。
-
• 寻找静态文件默认不设置任何静态文件路径,
Jinja2
会在项目的static
文件夹中寻找静态文件。也可以设置其他的路径,在初始化蓝图的时候,Blueprint
这个构造函数,有一个参数static_folder
可以指定静态文件的路径。
bp = Blueprint('admin',__name__,url_prefix='/admin',static_folder='static')
static_floder
可以是相对路径(相对蓝图文件所在的目录),也可以是绝对路径。
在模版中引用蓝图,使用蓝图名 + . + static
来引用。
<link href="{{ url_for('admin.static',filename='about.css') }}">
-
• 寻找模版文件跟静态文件一样,默认不设置任何模版文件的路径,将会在项目的
templates
中寻找模版文件。也可以设置其他的路径,在构造函数Blueprint
中有一个template_floder
参数可以设置模版的路径。
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
模版文件和静态文件有点区别,以上代码实装以后,如果渲染一个模版return render_template('admin.html')
,Flask
默认会去项目根目录下的templates
文件夹中查找admin.html
文件,如果找到了就直接返回,如果没有找到,才会去蓝图文件所在的目录下的templates
文件夹下去寻找。
-
• url_for生成url用
url_for
生成蓝图的url
,使用的格式是:蓝图名称 + . + 视图函数名称
。比如要获取admin
这个蓝图下的index
视图函数的url
。
url_for('admin.index')
其中这个蓝图名称是在创建蓝图的时候,传入的第一个参数。
bp = Blueprint('admin',__name__,url_prefix='/admin',template_folder='templates')
-
• 子域名子域名在很多网站中都会用到,比如一个网站叫做
xxx.com
,那么我们可以定义一个子域名oa.xxx.com
来作为oa
管理系统的网址,子域名的实现一般也会通过蓝图来实现,在之前的章节中,创建蓝图的时候添加了一个url_prefix=/user
作为url前缀,那样我们就可以通过/usr/
来访问user
下的url。但使用子域则不需要。另外,还需要配置SERVER_NAME
,比如app.config[SERVER_NAME]='example.com:9000'
。并且在注册蓝图的时候,还需要添加一个subdomain
的参数,这个参数就是子域名的名称。 -
• 参考示例admin.py
from flask import Blueprint
bp = Blueprint('admin',__name__,subdomain='admin')
@bp.route('/')
def admin():
return 'Admin Page'
app.py
from flask import Flask
import admin
# 配置`SERVER_NAME`
app.config['SERVER_NAME'] = 'example.com:8000'
# 注册蓝图,指定了subdomain
app.register_blueprint(admin.bp)
if __name__ == '__main__':
app.run(host='0.0.0.0',port=8000,debug=True)
实装以上两个文件后,还是不能正常的访问admin.example.com:8000
这个子域名,因为我们没有在host
文件中添加域名解析,可以在最后添加一行127.0.0.1 admin.example.com
,就可以访问了。另外,子域名不能在127.0.0.1
上出现,也不能在localhost
上出现。
来源:网络
欢迎关注我的公众号“壹葉筆記”,技术文章第一时间推送。
原文始发于微信公众号(壹葉筆記):【Flask】扩展:Jinja2 模板(三)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36102.html