模板
什么是模板引擎?
实现视图的业务逻辑和返回给前端的页面逻辑分离的工具,我们称之为模板引擎。
为什么要有模板引擎?
如果没有模板引擎,直接把模板的html代码写在视图函数里面,会给程序员的日常开发带来多大的困扰,模板引擎帮我们分开了业务逻辑和页面逻辑,并且我们每一次修改一个大字符串会非常不方便,模板引擎还可以读取并执行模板中的特殊语法标记,并根据传入的数据将变量替换为实际值,然后返回给浏览器,这个过程我们称之为渲染。
Flask中如何使用模板引擎?
flask
使用jinjia2
作为框架的默认模板引擎,jinjia2
是基于Python
的模板引擎,功能比较类似以php
的smarty
,J2ee
的Freemarker
和velocity
。jinjia2
除了设置变量,还允许我们在模板中添加if判断,执行for迭代,调用函数等,以各种方式控制模板的输出。并且jinja2
不限制模板的格式为html
,可以是任何格式的文本文件。
模板引擎传参
传参的时候要应用render_template()
,利用render_template的第二个参数进行传参,该函数在定义的时候,第二个参数是可变长形参,所以在传值的时候我们可以传入多个关键字实参。
我们定义的视图函数内部调用渲染模板:
render_template('list.html',info=info,html="<h1>jsaon-gdx</h1>",html1=func1)
源码接收关键字实参:
def render_template(template_name_or_list, **context):
注意:
render_template传参的时候以关键字实参进行传参。可以传多个,可以使用**将字典打散成关键字实参。
def demo2():
context_dict = {"name": "mark",
"age": "mark",
"sex": "girl",
"other_info":{"tel":1365,
"qq":565656}}
return render_template('index.html',**context_dict)
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板传参</title>
</head>
<body>
{{name}}
{{age}}
{{sex}}
{{other_info.tel}}
{{other_info["qq"]}}
</body>
</html>
模板加载静态文件
在模板中加载静态文件的时候也要使用到url_for()
函数,去寻找具体的静态文件资源。第一个参数是定位到static
文件夹,filename
参数定位到static文件夹内的具体资源。
{{ url_for('static',filename='相对于static文件夹的路径') }}
实例:
项目目录:
│ app.py
│
├─static # 文件夹
│ ├─css # 文件夹
│ │ demo.css
│ │
│ ├─images # 文件夹
│ │ 1.png
│ │
│ └─js # 文件夹
│ demo.js
│
├─templates # 文件夹
index.html
app.py
...
@app.route('/')
def hello_world():
return render_template('index.html')
...
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>静态文件加载</title>
<link rel="stylesheet" href="{{ url_for('static',filename='css/demo.css') }}">
<script src="{{ url_for('static',filename='js/demo.js') }}"></script>
</head>
<body>
<img src="{{ url_for('static',filename='images/1.png') }}">
</body>
</html>
demo.css
body{
background: red;
}
demo.js
alert('hello world')
模板的继承
jinja2的模板的继承可以把一些公共的代码定义到一个基模板中,以后所有的子模板直接继承基模板,在子模板被渲染是会自动包含继承基模板的内容,通过模板的继承可以避免在多个模板中编写重复代码。
具体实现:
在基模板中定义一些公共的代码,子模板会继承这些公共的代码,但是子模板需要根据自己的需求去实现不同的代码,这个时候就需要在基模板中提供一些接口,以便子模板实现自己的业务需求。
1.基本写法
在基/父模板中定义接口(block)
{% block main %} {# main是自定义的变量名 #}
{% endblock %}
在子模板中继承父模板,并且重写接口(block)
{% extends 'base.html' %} {# extends 后面跟的参数是导入的基模板相对于templates的路径 #}
{% block main %}
{% endblock %}
2.子模板中调用父模板代码block中的代码
基模板中
{% block main %}
<p>父模板中main中原有的内容</p>
{% endblock %}
子模板中:
{% block main %}
{{ super() }} {# 保留基模板中本块的代码 #}
<p>子模板中重写main的内容 </p>
{% endblock %}
3.在子模板中调用其他block中的代码:
子模板中:
{% block main %}
{{ self.demo() }} {# self.其他block名字 #}
<p>子模板中重写main的内容 </p>
{% endblock %}
4.子模板中的想定义自己的代码只能放到block中,否则无效
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父模板</title>
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap.css') }}">
</head>
<body>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<div style=" visibility:hidden;display:block;font-size:0;clear:both;
height:50px"></div>
{% block main %}
<p>父模板中main中原有的内容</p>
{% endblock %}
<br>
<br>
<br>
{% block demo %}
<p>demo中原有的内容</p>
{% endblock %}
<div style=" visibility:hidden;display:block;font-size:0;clear:both;
height:0"></div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li>
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
<li><a href="#">1</a></li>
<li><a href="#">2</a></li>
<li><a href="#">3</a></li>
<li><a href="#">4</a></li>
<li><a href="#">5</a></li>
<li>
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
</ul>
</nav>
</body>
</html>
detail.html
{% extends 'base.html' %}
{% block demo %}
<p>子模板中重写demo的内容</p>
{% endblock %}
{% block main %}
{{ super() }} {# 保留基模板中本block的代码 #}
{{ self.demo() }} {# 调用demo block的代码 #}
<p>子模板中重写main的内容 </p>
{% endblock %}
app.py 注意:app.config.update(TEMPLATE_AUTO_RELOAD=True)语句用于每次都重新加载模板文件
from flask import Flask, render_template
app = Flask(__name__)
app.config.update(TEMPLATES_AUTO_RELOAD=True)
@app.route('/')
def hello_world():
return render_template('base.html')
@app.route('/demo/')
def demo():
return render_template('detail.html')
if __name__ == '__main__':
app.run()
子模板运行效果
模板小案例
flask框架前端的反向解析用的也是后端的url_for("函数名"),<a href="{{url_for('sb',nid=k)}}">查看详细</a>可以解析出路由和浏览器传来的动态路由(nid=k),点击a标签跳转路由。
Markup:flask中后端过滤器,表明后端Markup传来的html是安全的,可以渲染。
{{html|safe}} :后端没有使用Markup过滤器表明html是安全的,前端可以使用模板过滤器,渲染html,类似Django的过滤器。
后端传的变量是一个函数名(html1=func1)给前端,前端拿到函数名加括号使用模板语法就可以调用后端写的函数,还可以在模板语法中给该函数传参,例:{{html1("-DSB","-SB")}}。Django中的前端使用函数或类进行渲染是不需要加括号的,内部会自动加括号调用函数或实例化一个对象。
render_template('box/login.html')的路径是template文件夹的相对路径
from flask import Flask,render_template,request,redirect,session,url_for,Markup
app = Flask(__name__)
app.debug = True
app.secret_key = 'sdfsdfsdfsdf'
USERS = {
1:{'name':'张三','age':18,'gender':'男','text':"道路千万条"},
2:{'name':'李四','age':28,'gender':'男','text':"安全第一条"},
3:{'name':'王五','age':18,'gender':'女','text':"行车不规范"},
}
def func1(st,st1):
return Markup(f"<h1>jsaon-gdx{st}{st1}</h1>") # flask中后端过滤器,表明后端Markup传来的html是安全的,可以渲染。
@app.route('/list',methods=['GET'])
def list():
info=USERS
return render_template('list.html',info=info,html="<h1>jsaon-gdx</h1>",html1=func1)
@app.route('/detail/<int:nid>',methods=['GET'],endpoint="sb")
def detail(nid):
return "ok"
if __name__ == '__main__':
app.run()
<!--list.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for k,v in info.items() %}
<tr>
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['name']}}</td>
<td>{{v.get('name')}}</td>
<td><a href="{{url_for('sb',nid=k)}}">查看详细</a></td>
</tr>
{% endfor %}
{{html|safe}}
{{html1("-DSB","-SB")}}
</body>
</html>
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{sb(1,2)}}
{{1|db(2,3)}}
</body>
</html>