flask jinja2和tornado模板的使用

 

一:渲染模版

要渲染一个模板,通过render_template方法即可。

from flask import Flask,render_template

from livereload import Server

app = Flask(__name__)

@app.route('/')

def index():

      return render_template('index.html',title="<h1>hello world</h1>")

@app.template_filter('md')

def markdown_to_html(txt):

    from markdown import markdown

    return markdown

@app.route('/about/')
def about():
  # return render_template('about.html',user='username')
  return render_template('about.html',**{'user':'username'})

if __name__ == '__main__':

    live_server = Server(app.wsgi_app)

    live_server.watch('**/*.*')

    live_server.server(open_rul=True)

渲染模版时有两种传递参数的方式:用 var='value' 传递一个参数;使用字典组织多个参数,并且加两个*号转换成关键字参数传入。

  • {{ ... }}:装载一个变量,模板渲染的时候,会使用传进来的同名参数这个变量代表的值替换掉。
  • {% ... %}:装载一个控制语句。
  • {# ... #}:装载一个注释,模板渲染的时候会忽视这中间的值。

八:过滤器

过滤器是通过(|)符号进行使用的,例如:{{ name|length }}:将返回name的长度。

过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。

 常用的过滤器有:

过滤器名称    说明    
safe 渲染时值不转义
capitialize 把值的首字母转换成大写,其他子母转换为小写
 lower 把值转换成小写形式 
 upper 把值转换成大写形式 
 title 把值中每个单词的首字母都转换成大写
 trim 把值的首尾空格去掉
 striptags 渲染之前把值中所有的HTML标签都删掉
join  拼接多个值为字符串
 replace 替换字符串的值
 round 默认对数字进行四舍五入,也可以用参数进行控制
int  把值转换成整型

Jinja2拥有许多过滤器:(转自:http://www.jianshu.com/p/31a75d3d9270)

  • abs(value):返回一个数值的绝对值。示例:-1|abs
  • default(value,default_value,boolean=false):如果当前变量没有值,则会使用参数中的值来代替。示例:name|default('xiaotuo')——如果name不存在,则会使用xiaotuo来替代。boolean=False默认是在只有这个变量为undefined的时候才会使用default中的值,如果想使用python的形式判断是否为false,则可以传递boolean=true。也可以使用or来替换。
  • escape(value)或e:转义字符,会将<>等符号转义成HTML中的符号。示例:content|escapecontent|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):计算一个长字符串中单词的个数。

十:转义

在模板渲染字符串的时候,字符串有可能包括一些非常危险的字符比如<>等,这些字符会破坏掉原来HTML标签的结构,更严重的可能会发生XSS跨域脚本攻击,因此如果碰到<>这些字符的时候,应该转义成HTML能正确表示这些字符的写法。

对于一些不信任的字符串,可以通过{{ content_html|e }}或者是{{ content_html|escape }}的方式进行转义。

如果想关闭自动转义,可以通过{{ content_html|safe }}的方式关闭自动转义。(过滤器:一个过滤器及一个函数,和smarty类似)

{%autoescape true/false%}...{%endautoescape%}可以将一段代码块放在中间,来关闭或开启自动转义:

return render_template('about.html',will_not_be_escaped='<h1>标题</h1>')

{% autoescape false %}
<p>autoescaping is disabled here
<p>{{ will_not_be_escaped }}
{% endautoescape %}

jinja2的控制结构

控制语句都是放在{% ... %}中,并且有一个语句{% endxxx %}来进行结束。

  jinja2中的if语句类似与Python的if语句,它也具有单分支,多分支等多种结构,不同的是,条件语句不需要使用冒号结尾,而结束控制语句,需要使用endif关键字。

 

{% if daxin.safe %}

daxin is safe.

{% elif daxin.dead %}

daxin is dead

{% else %}

daxin is okay

{% endif %}

 

jinja2的for循环

  jinja2中的for循环用于迭代Python的数据类型,包括列表,元组和字典。在jinja2中不存在while循环。

  迭代列表

1

2

3

4

5

<ul>

{% for user in users %}

<li>{{ user.username|title }}</li>

{% endfor %}

</ul>

  迭代字典

1

2

3

4

5

6

<dl>

{% for key, value in my_dict.iteritems() %}

<dt>{{ key }}</dt>

<dd>{{ value}}</dd>

{% endfor %}

</dl>

      当然也可以加入else语句,在循环正确执行完毕后,执行

      在for循环中,jinja2还提供了一些特殊的变量,用以来获取当前的遍历状态:

Jinja2中for循环内置常量

变量描述
loop.index当前迭代的索引(从1开始)
loop.index0当前迭代的索引(从0开始)
loop.first是否是第一次迭代,返回bool
loop.last是否是最后一次迭代,返回bool
loop.length序列中的项目数量
loop.revindex到循环结束的次数(从1开始)
loop.revindex0到循环结束的次数(从0开始)

2:for循环

1)普通用法

<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>

2)遍历字典

{% for key, value in my_dict.iteritems() %}
<dt>{{ key|e }}</dt>
<dd>{{ value|e }}</dd>
{% endfor %}

3)在循环中加入else

<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% else %}
<li><em>no users found</em></li>
{% endfor %}
</ul>

五:运算符

  • +号运算符:可以完成数字相加,字符串相加,列表相加。但是并不推荐使用+运算符来操作字符串,字符串相加应该使用~运算符。
  • -号运算符:只能针对两个数字相减。
  • /号运算符:对两个数进行相除。
  • %号运算符:取余运算。
  • *号运算符:乘号运算符,并且可以对字符进行相乘。
  • **号运算符:次幂运算符,比如2**3=8。
  • in操作符:跟python中的in一样使用,比如{{1 in [1,2,3]}}返回true
  • ~号运算符:拼接多个字符串,比如{{"Hello" ~ "World"}}将返回HelloWorld

三:变量

1)在模板中添加变量,可以使用(set)语句。

{% set name='xx' %}

之后就可以在页面文件中使用name这个变量了。在解释性语言中,变量的类型时运行时确定的,因此,这里的变量可以赋任何类型的值。

 

上面的语句创建的是全局变量,从定义之后的文件部分中都可以访问 。

2)局部变量

可以使用with语句来创建一个内部的作用域,将set语句放在其中,这样创建的变量只在with代码块中才有效。

{% with foo = 42 %}
{{ foo }}
{% endwith %}

这样,foo变量就只能在with标签间可以使用。

七:模版文件的继承

模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在父模板中,并且父模板通过定义block给子模板开一个口,子模板根据需要,再实现这个block进行具体内容定义。

比如:父模版base.html如下:

<!DOCTYPE html>
<html lang="en">
<head>
{% block head %} //开放一个地方,以待具体赋值
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2008 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>

然后定义子模版,对父模板中的block部分进行覆盖书写

{% extends "base.html" %}//1:继承父模板
{% block title %}Index{% endblock %}//2:书写title block
{% block head %}//3:书写head block
{{ super() }}//调用父模板中的内容,如果不调用,则此处会被子模板中书写的内容覆盖掉
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}//4:书写content block
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}

另外:模板文件中对block内容的调用,可以使用 self.blockName 的方式。

<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>//调用title block的内容

注意:在子模板中,所有的标签和代码都要添加到从父模板中继承的block中。否则,这些文本和标签将不会被渲染。(因为子模板相当于把内容嵌入到父模板到block中,而没有写到block中的内容当然不会被嵌入,也就不会被渲染。)

TORNADO模板语法

变量与表达式

在tornado的模板中使用{{}}作为变量或表达式的占位符,使用render渲染后占位符{{}}会被替换为相应的结果值。

我们将index.html中的一条房源信息记录

<li class="house-item">
    <a href=""><img src="/static/images/home01.jpg"></a>
    <div class="house-desc">
        <div class="landlord-pic"><img src="/static/images/landlord01.jpg"></div>
        <div class="house-price">¥<span>398</span>/晚</div>
        <div class="house-intro">
            <span class="house-title">宽窄巷子+160平大空间+文化保护区双地铁</span>
            <em>整套出租 - 5分/6点评 - 北京市丰台区六里桥地铁</em>
        </div>
    </div>
</li>

改为模板:

<li class="house-item">
    <a href=""><img src="/static/images/home01.jpg"></a>
    <div class="house-desc">
        <div class="landlord-pic"><img src="/static/images/landlord01.jpg"></div>
        <div class="house-price">¥<span>{{p1 + p2}}</span>/晚</div>
        <div class="house-intro">
            <span class="house-title">{{"+".join(titles)}}</span>
            <em>整套出租 - {{score}}分/{{comments}}点评 - {{position}}</em>
        </div>
    </div>
</li>

 渲染方式如下:

class IndexHandler(RequestHandler):
    def get(self):
        house_info = {
            "p1": 198,
            "p2": 200,
            "titles": ["宽窄巷子", "160平大空间", "文化保护区双地铁"],
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        }
        self.render("index.html", **house_info)

 

控制语句

可以在Tornado模板中使用Python条件和循环语句。控制语句以{\%和\%}包围,并以类似下面的形式被使用:

{% if page is None %}

{% if len(entries) == 3 %}

控制语句的大部分就像对应的Python语句一样工作,支持if、for、while,注意end:

{% if ... %} ... {% elif ... %} ... {% else ... %} ... {% end %}
{% for ... in ... %} ... {% end %}
{% while ... %} ... {% end %}

再次修改index.html:

<ul class="house-list">
    {% if len(houses) > 0 %}
        {% for house in houses %}
        <li class="house-item">
            <a href=""><img src="/static/images/home01.jpg"></a>
            <div class="house-desc">
                <div class="landlord-pic"><img src="/static/images/landlord01.jpg"></div>
                <div class="house-price">¥<span>{{house["price"]}}</span>/晚</div>
                <div class="house-intro">
                    <span class="house-title">{{house["title"]}}</span>
                    <em>整套出租 - {{house["score"]}}分/{{house["comments"]}}点评 - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        对不起,暂时没有房源。
    {% end %}
</ul>

python中渲染语句为:

class IndexHandler(RequestHandler):
    def get(self):
        houses = [
        {
            "price": 398,
            "title": "宽窄巷子+160平大空间+文化保护区双地铁",
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        },
        {
            "price": 398,
            "title": "宽窄巷子+160平大空间+文化保护区双地铁",
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        },
        {
            "price": 398,
            "title": "宽窄巷子+160平大空间+文化保护区双地铁",
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        },
        {
            "price": 398,
            "title": "宽窄巷子+160平大空间+文化保护区双地铁",
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        },
        {
            "price": 398,
            "title": "宽窄巷子+160平大空间+文化保护区双地铁",
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        }]
        self.render("index.html", houses=houses)

2-3 函数

static_url()

Tornado模板模块提供了一个叫作static_url的函数来生成静态文件目录下文件的URL。如下面的示例代码:

<link rel="stylesheet" href="{{ static_url("style.css") }}">

这个对static_url的调用生成了URL的值,并渲染输出类似下面的代码:

<link rel="stylesheet" href="/static/style.css?v=ab12">

优点:

  • static_url函数创建了一个基于文件内容的hash值,并将其添加到URL末尾(查询字符串的参数v)。这个hash值确保浏览器总是加载一个文件的最新版而不是之前的缓存版本。无论是在你应用的开发阶段,还是在部署到生产环境使用时,都非常有用,因为你的用户不必再为了看到你的静态内容而清除浏览器缓存了。
  • 另一个好处是你可以改变你应用URL的结构,而不需要改变模板中的代码。例如,可以通过设置static_url_prefix来更改Tornado的默认静态路径前缀/static。如果使用static_url而不是硬编码的话,代码不需要改变。

转义

我们新建一个表单页面new.html

<!DOCTYPE html>
<html>
    <head>
        <title>新建房源</title>
    </head>
    <body>
        <form method="post">
            <textarea name="text"></textarea>
            <input type="submit" value="提交">
        </form>
        {{text}}
    </body>
</html>

对应的handler为:

class NewHandler(RequestHandler):

    def get(self):
        self.render("new.html", text="")

    def post(self):
        text = self.get_argument("text", "") 
        print text
        self.render("new.html", text=text)

当我们在表单中填入如下内容时:

<script>alert("hello!");</script>

form_input_js.pnguploading.4e448015.gif转存失败重新上传取消注入js

写入的js程序并没有运行,而是显示出来了:

display_js_code.pnguploading.4e448015.gif转存失败重新上传取消显示了js代码

我们查看页面源代码,发现<、>、"等被转换为对应的html字符。

&lt;script&gt;alert(&quot;hello!&quot;);&lt;/script&gt;

这是因为tornado中默认开启了模板自动转义功能,防止网站受到恶意攻击。

我们可以通过raw语句来输出不被转义的原始格式,如:

{% raw text %}

注意:在Firefox浏览器中会直接弹出alert窗口,而在Chrome浏览器中,需要set_header("X-XSS-Protection", 0)

若要关闭自动转义,一种方法是在Application构造函数中传递autoescape=None,另一种方法是在每页模板中修改自动转义行为,添加如下语句:

{% autoescape None %}

escape()

关闭自动转义后,可以使用escape()函数来对特定变量进行转义,如:

{{ escape(text) }}

自定义函数

在模板中还可以使用一个自己编写的函数,只需要将函数名作为模板的参数传递即可,就像其他变量一样。

我们修改后端如下:

def house_title_join(titles):
    return "+".join(titles)

class IndexHandler(RequestHandler):
    def get(self):
        house_list = [
        {
            "price": 398,
            "titles": ["宽窄巷子", "160平大空间", "文化保护区双地铁"],
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        },
        {
            "price": 398,
            "titles": ["宽窄巷子", "160平大空间", "文化保护区双地铁"],
            "score": 5,
            "comments": 6,
            "position": "北京市丰台区六里桥地铁"
        }]
        self.render("index.html", houses=house_list, title_join = house_title_join)

前段模板我们修改为:

<ul class="house-list">
    {% if len(houses) > 0 %}
        {% for house in houses %}
        <li class="house-item">
            <a href=""><img src="/static/images/home01.jpg"></a>
            <div class="house-desc">
                <div class="landlord-pic"><img src="/static/images/landlord01.jpg"></div>
                <div class="house-price">¥<span>{{house["price"]}}</span>/晚</div>
                <div class="house-intro">
                    <span class="house-title">{{title_join(house["titles"])}}</span>
                    <em>整套出租 - {{house["score"]}}分/{{house["comments"]}}点评 - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        对不起,暂时没有房源。
    {% end %}
</ul>

2-4 块

我们可以使用块来复用模板,块语法如下:

{% block block_name %} {% end %}

现在,我们对模板index.html进行抽象,抽离出父模板base.html如下:

<!DOCTYPE html>
<html>
<head> 
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    {% block page_title %}{% end %}
    <link href="{{static_url('plugins/bootstrap/css/bootstrap.min.css')}}" rel="stylesheet">
    <link href="{{static_url('plugins/font-awesome/css/font-awesome.min.css')}}" rel="stylesheet">
    <link href="{{static_url('css/reset.css')}}" rel="stylesheet">
    <link href="{{static_url('css/main.css')}}" rel="stylesheet">
    {% block css_files %}{% end %}
</head>
<body>
    <div class="container">
        <div class="top-bar">
            {% block header %}{% end %}
        </div>
        {% block body %}{% end %}
        <div class="footer">
            {% block footer %}{% end %}
        </div>
    </div>

    <script src="{{static_url('js/jquery.min.js')}}"></script>
    <script src="{{static_url('plugins/bootstrap/js/bootstrap.min.js')}}"></script>
    {% block js_files %}{% end %}
</body>
</html>

而子模板index.html使用extends来使用父模板base.html,如下:

{% extends "base.html" %}

{% block page_title %}
    <title>爱家-房源</title>
{% end %}

{% block css_files %}
    <link href="{{static_url('css/index.css')}}" rel="stylesheet">
{% end %} 

{% block js_files %}
    <script src="{{static_url('js/index.js')}}"></script>
{% end %}

{% block header %}
    <div class="nav-bar">
        <h3 class="page-title">房 源</h3>
    </div>
{% end %}

{% block body %}
    <ul class="house-list">
    {% if len(houses) > 0 %}
        {% for house in houses %}
        <li class="house-item">
            <a href=""><img src="/static/images/home01.jpg"></a>
            <div class="house-desc">
                <div class="landlord-pic"><img src="/static/images/landlord01.jpg"></div>
                <div class="house-price">¥<span>{{house["price"]}}</span>/晚</div>
                <div class="house-intro">
                    <span class="house-title">{{title_join(house["titles"])}}</span>
                    <em>整套出租 - {{house["score"]}}分/{{house["comments"]}}点评 - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        对不起,暂时没有房源。
    {% end %}
    </ul>
{% end %}

{% block footer %}
    <p><span><i class="fa fa-copyright"></i></span>爱家租房&nbsp;&nbsp;享受家的温馨</p>
{% end %}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值