模板是⼀个web开发必备的模块。因为我们在渲染⼀个⽹⻚的时候,并不是只渲染⼀个纯⽂本字符串,⽽是需要渲染⼀个有富⽂本标签的⻚⾯。这时候我们就需要使⽤模板了。在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。这个模板⾮常的强⼤,并且执⾏效率⾼。
render_template
方法:
注意: flask默认.html
文件在templates文件夹下,如果要更改模板文件地址,应在创建时传递template_folder
,指定具体路径,详见代码示例2。
代码示例1:
# render_template需要导入
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/about/')
def about():
return render_template('about.html')
代码示例2:
from flask import Flask,render_template
app = Flask(__name__,template_folder=r'C:\templates')
@app.route('/about/')
def about():
return render_template('about.html')
注意:模板文件自动更新
# 模板文件自动更新
app.config['TEMPLATES_AUTO_RELOAD'] = True
当传递参数极少时,可以直接传递参数render_template("xxx.html", username="xxxx")
代码示例:
# .py文件
return render_template("index.html", username="xxxx")
# index.html文件
{
{
username}}
当传递参数较多时,可以采取dict的方式
代码示例:
# .py文件
# 用字典,key:value的形式,定义参数
context = {
xxx: "xxx"
}
return render_template("index.html", context=context)
# index.html文件
{
{
context.xxx}}
**contex
t的方式该方式是render_template中的一种方法,能将dict中自动改为xx1=xx1,xx2=xx2的关键字参数。减少.html中的代码量
代码示例:
# .py文件
context = {
xxx: "xxx"
}
return render_template("index.html", **context)
# .html文件
{
{
xxx }}
介绍:
过滤器是通过管道符号(|)进⾏使⽤的,例如:{ { name|length }},将返回name的⻓度。过滤器相当于是⼀个函数,把当前的变量传⼊到过滤器中,然后过滤器根据⾃⼰的功能,再返回相应的值,之后再将结果渲染到⻚⾯中。Jinja2中内置了许多过滤器,在这⾥可以看到所有的过滤器。
Flask中内置的所有过滤器:
abs(value)
:返回⼀个数值的绝对值。default(value,default_value,boolean=false)
:如果当前变量没有值,则会使⽤参数中的值来代替。name|default('name1')
——如果name不存在,则会使⽤juran来替代。boolean=False
默认是在只有这个变量为undefined的时候才会使⽤default中的值,如果想使⽤python的形式判断是否为false,则可以传递boolean=true
。也可以使⽤or来替换。escape(value)或e
:转义字符,会将<、>等符号转义成HTML中的符号。例如:content|escape
或content|e
。first(value)
:返回⼀个序列的第⼀个元素。names|first
。format(value,*arags,**kwargs)
:格式化字符串。例如以下代码:{ { "%s" - "%s"|format('Hello?',"Foo!") }}将输出:Helloo? - Foo!
last(value)
:返回⼀个序列的最后⼀个元素。示例:names|last
。length(value)
:返回⼀个序列或者字典的⻓度。示例:names|length
。join(value,d=u'')
:将⼀个序列⽤d这个参数的值拼接成字符串。safe(value)
:如果开启了全局转义,那么safe过滤器会将变量关掉转义。示例:content_html|safe
。int(value)
:将值转换为int类型。float(value)
:将值转换为float类型。lower(value)
:将字符串转换为⼩写。upper(value)
:将字符串转换为⼩写。replace(value,old,new)
: 替换将old替换为new的字符串。truncate(value,length=255,killwords=False)
:截取length⻓度的字符串。striptags(value)
:删除字符串中所有的HTML标签,如果出现多个空格,将替换成⼀个空格。trim
:截取字符串前⾯和后⾯的空⽩字符。string(value)
:将变量转换成字符串。wordcount(s)
:计算⼀个⻓字符串中单词的个数。自定义过滤器:
如果自带的所有过滤器不能满足需求,可以用@app.template_filter('自定义过滤器名字')
自定义flask过滤器。
使用示例:
实现类似于下图评论中多少分钟前发布效果。
代码示例:
# .py 中
@app.template_filter("test")
def handle_time(time):
"""
小于1分钟=> 刚刚
大于1分钟小于1小时=>xx分钟之前
大于1小时小于24小时=>xxx小时之前
"""
if isinstance(time, datetime):
now = datetime.now()
# total_seconds 得到总秒数
timestamp = (now - time).total_seconds()
if timestamp < 60:
return "刚刚"
elif timestamp >=60 and timestamp <= 60*60:
return "%s分钟之前" % int(timestamp/60)
elif timestamp>= 60*60 and timestamp <= 60*60*24:
return "%s小时之前" % int(timestamp/(60*60))
else:
return time
# .html中
<p>文章发表时间:{
{
now_time|test }}</p>
介绍:
每一种语言都有自己的控制语句,if…else之类的。
Flask种也有自己的控制语句,并且所有的控制语句都是放在{% ... %}
中,并且有⼀个语句{% endxxx %}
来进⾏结束,Jinja中常⽤的控制语句有if/for..in..
if
if
:
if语句和python中的类似,可以使⽤>,<,<=,>=,==,!=来进⾏判断,也可以通过and,or,not,()来进⾏逻辑合并操作。
代码示例:
{
% if kenny.sick %}
Kenny is sick.
{
% elif kenny.dead %}
You killed Kenny! You bastard!!!
{
% else %}
Kenny looks okay --- so far
{
% endif %}
for...in...
for...in...
:
for循环可以遍历任何⼀个序列包括列表、字典、元组。并且可以进⾏反向遍历。
代码示例1:普通的遍历
<ul>
{
% for user in users %}
<li>{
{
user }}</li>
{
% endfor %}
</ul>
代码示例2:遍历字典
<dl>
{
% for key, value in my_dict.items() %}
<dt>{
{
key }}</dt>
<dd>{
{
value }}</dd>
{
% endfor %}
</dl>
代码示例3:如果序列中没有值的时候,进⼊else
<ul>
{
% for user in users %}
<li>{
{
user.username }}</li>
{
% else %}
<li><em>no users found</em></li>
{
% endfor %}
</ul>
for循环中可用来获取当前遍历状态的变量:
变量 | 描述 |
---|---|
loop.index |
当前迭代的索引(从1开始) |
loop.index0 |
当前迭代的索引(从0开始) |
loop.first |
是否是第⼀次迭代,返回True或False |
loop.last |
是否是最后⼀次迭代,返回True或False |
loop.length |
序列的⻓度 |
代码示例:
# .py
@app.route("/")
def index():
context = {
"username": "zero1",
"books": ["Python", "PHP", "Java"],
"users": {
"name": "zero2",
"age": 18,
"address": "csc"
}
}
return render_template("if_for.html", **context)
# .html
<hr>
{
% for value in users.values() %}
<p>{
{
loop.first }}</p> # 返回Flase
<p>{
{
value }}</p>
{
% endfor %}
注意: 不可以使⽤continue和break表达式来控制循环的执⾏。
宏介绍:
模板中的宏跟python中的函数类似,可以传递参数,但是不能有返回值,可以将⼀些经常⽤到的代码⽚段放到宏中,然后把⼀些不固定的值抽取出来当成⼀个变量。
{
% macro input(name, value='', type='text') %}
<input type="{
{ type }}" name="{
{ name }}" value="{
{ value }}">
{
% endmacro %}
以上例⼦可以抽取出了⼀个input标签,指定了⼀些默认参数。那么我们以后创建input标签的时候,可以通过他快速的创建:
<p>{
{
input('username') }}</p>
<p>{
{
input('password', type='password') }}</p>
import语句介绍:
在真实的开发中,会将⼀些常⽤的宏单独放在⼀个⽂件中,在需要使⽤的时候,再从这个⽂件中进⾏导⼊。import语句的⽤法跟python中的import类似,可以直接import...as...
,也可以from...import...
或者from...import...as...
,假设现在有⼀个⽂件,叫做forms.html,⾥⾯有两个宏分别为input和textarea。
forms.html:
{
% macro input(name, value='', type='text') %}
<input type="{
{ type }}" value="{
{ value|e }}" name="{
{ name }}">
{
% endmacro %}
{
% macro textarea(name, value='', rows=10, cols=40) %}
<textarea name="{
{ name }}" rows="{
{ rows }}" cols="{
{
cols
}}">{
{
value|e }}</textarea>
{
% endmacro %}
import...as...
形式:
{
% import 'macro.html' as macro %}
<tr>
<td>用户名:</td>
<td>{
{
macro.input('username') }}</td>
</tr>
from...import...as.../from...import...
形式
{
% from "macro.html" import input %}
<tr>
<td>密码:</td>
<td>{
{
input("password",type="password") }}</td>
</tr>
注意: 导⼊模板并不会把当前上下⽂中的变量添加到被导⼊的模板中,如果你想要导⼊⼀个需要访问当前上下⽂变量的宏,有两种可能的⽅法
与上下⽂⼀起(with context)导⼊宏的方式:
{
% import 'macro.html' as macro with context %}
介绍: include
语句可以把⼀个模板引⼊到另外⼀个模板中,类似于把⼀个模板的代码copy到另外⼀个模板的指定位置。
代码示例:
{
% include 'header.html' %}
主体内容
{
% include 'footer.html' %}
该方法可用于导入重复部分,如常见的网页头和网页底部。减少代码重复量。
介绍: 赋值语句(set)可以为模板中添加变量。
代码示例1:
{
% set name='zero' %}
那么以后就可以使⽤name
来代替zero
这个值了,同时,也可以给他赋值为列表和元组:
代码示例2:
{
% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
赋值语句创建的变量在其之后都是有效的,如果不想让⼀个变量污染全局环境,可以使⽤with语句来创建⼀个内部的作⽤域,将set语句放在其中,这样创建的变量只在with代码块中才有效。
代码示例3:
{
% with %}
{
% set foo = 42 %}
{
{
foo }} foo is 42 here
{
% endwith %}
也可以在with的后⾯直接添加变量,⽐如以上的写法可以修改成这样:
代码示例4:
{
% with foo = 42 %}
{
{
foo }}
{
% endwith %}
这代码3、代码4两种方式都是等价的,一旦超出with代码块,就不能再使用foo这个变量了。
介绍:
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>
以上⽗模板中,抽取了所有模板都需要⽤到的元素html、body等,并且对于⼀些所有模板都要⽤到的样式⽂件style.css也进⾏了抽取,同时对于⼀些⼦模板需要重写的地⽅,⽐如title、head、body都定义成了block,然后
⼦模板可以根据⾃⼰的需要,再具体的实现。
代码示例:
{
% extends "base.html" %}
{
% block title %}首页{
% endblock %}
{
% block head %}
{
{
super() }}
<style type="text/css">
.detail{
color: red;
}
</style>
{
% endblock %}
{
% block content %}
<h1>这里是首页</h1>
<p class="detail">
首页的内容
</p>
{
% endblock %}
⾸先第⼀⾏就定义了⼦模板继承的⽗模板,并且可以看到⼦模板实现了title
这个block,并填充了⾃⼰的内容,再看head这个block,⾥⾯调⽤了
super()这个函数,这个函数的⽬的是执⾏⽗模板中的代码,把⽗模板中的内容添加到⼦模板中,如果没有这⼀句,则⽗模板中处在head这个block中的代码将会被⼦模板中的代码给覆盖掉。
另外,模板中不能出现重名的block,如果⼀个地⽅需要⽤到另外⼀个block中的内容,可以使⽤self.blockname的⽅式进⾏引⽤。
<title>
{
% block title %}
这是标题
{
% endblock %}
</title>
<h1>{
{
self.title() }}</h1>
以上示例中h1标签重⽤了title这个block中的内容,⼦模板实现了title这个block,h1标签也能拥有这个值。
另外,在⼦模板中,所有的⽂本标签和代码都要添加到从⽗模板中继承的
block中。否则,这些⽂本和标签将不会被渲染。
介绍:
Web应⽤中会出现⼤量的静态⽂件来使得⽹⻚更加⽣动美观。类似于CSS样式⽂件、JavaScript脚本⽂件、图⽚⽂件、字体⽂件等静态资源。在Jinja中加载静态⽂件⾮常简单,只需要通过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这个⽂件夹下寻找