Tornado之模板

知识点

  • 静态文件配置

    • static_path
    • StaticFileHandler
  • 模板使用
    • 变量与表达式
    • 控制语句
    • 函数

4.1 静态文件

现在有一个预先写好的静态页面文件 (下载静态文件资源), 我们来看下如何用tornado提供静态文件。

static_path

我们可以通过向web.Application类的构造函数传递一个名为static_path的参数来告诉Tornado从文件系统的一个特定位置提供静态文件,如:

app = tornado.web.Application(
    [(r‘/‘, IndexHandler)],
    static_path=os.path.join(os.path.dirname(__file__), "statics"),
)

在这里,我们设置了一个当前应用目录下名为statics的子目录作为static_path的参数。现在应用将以读取statics目录下的filename.ext来响应诸如/static/filename.ext的请求,并在响应的主体中返回。

对于静态文件目录的命名,为了便于部署,建议使用static

对于我们提供的静态文件资源,可以通过http://127.0.0.1/static/html/index.html来访问。而且在index.html中引用的静态资源文件,我们给定的路径也符合/static/...的格式,故页面可以正常浏览。

<link href="/static/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/plugins/font-awesome/css/font-awesome.min.css" rel="stylesheet">
<link href="/static/css/reset.css" rel="stylesheet">
<link href="/static/css/main.css" rel="stylesheet">
<link href="/static/css/index.css" rel="stylesheet">

<script src="/static/js/jquery.min.js"></script>
<script src="/static/plugins/bootstrap/js/bootstrap.min.js"></script>
<script src="/static/js/index.js"></script>

StaticFileHandler

我们再看刚刚访问页面时使用的路径http://127.0.0.1/static/html/index.html,这中url显然对用户是不友好的,访问很不方便。我们可以通过tornado.web.StaticFileHandler来自由映射静态文件与其访问路径url。

tornado.web.StaticFileHandler是tornado预置的用来提供静态资源文件的handler。

import os

current_path = os.path.dirname(__file__)
app = tornado.web.Application(
    [
        (r‘^/()$‘, StaticFileHandler, {"path":os.path.join(current_path, "statics/html"), "default_filename":"index.html"}),
        (r‘^/view/(.*)$‘, StaticFileHandler, {"path":os.path.join(current_path, "statics/html")}),
    ],
    static_path=os.path.join(current_path, "statics"),
)
  • path 用来指明提供静态文件的根路径,并在此目录中寻找在路由中用正则表达式提取的文件名。
  • default_filename 用来指定访问路由中未指明文件名时,默认提供的文件。

现在,对于静态文件statics/html/index.html,可以通过三种方式进行访问:

  1. http://127.0.0.1/static/html/index.html
  2. http://127.0.0.1/
  3. http://127.0.0.1/view/index.html

4.2 使用模板

1. 路径与渲染

使用模板,需要仿照静态文件路径设置一样,向web.Application类的构造函数传递一个名为template_path的参数来告诉Tornado从文件系统的一个特定位置提供模板文件,如:

app = tornado.web.Application(
    [(r‘/‘, IndexHandler)],
    static_path=os.path.join(os.path.dirname(__file__), "statics"),
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
)

在这里,我们设置了一个当前应用目录下名为templates的子目录作为template_path的参数。在handler中使用的模板将在此目录中寻找。

现在我们将静态文件目录statics/html中的index.html复制一份到templates目录中,此时文件目录结构为:

.
├── statics
│   ├── css
│   │   ├── index.css
│   │   ├── main.css
│   │   └── reset.css
│   ├── html
│   │   └── index.html
│   ├── images
│   │   ├── home01.jpg
│   │   ├── home02.jpg
│   │   ├── home03.jpg
│   │   └── landlord01.jpg
│   ├── js
│   │   ├── index.js
│   │   └── jquery.min.js
│   └── plugins
│       ├── bootstrap
│       │   └─...
│       └── font-awesome
│           └─...
├── templates
│   └── index.html
└── test.py

在handler中使用render()方法来渲染模板并返回给客户端。

class IndexHandler(RequestHandler):
    def get(self):
        self.render("index.html") # 渲染主页模板,并返回给客户端。

current_path = os.path.dirname(__file__)
app = tornado.web.Application(
    [
        (r‘^/$‘, IndexHandler),
        (r‘^/view/(.*)$‘, StaticFileHandler, {"path":os.path.join(current_path, "statics/html")}),
    ],
    static_path=os.path.join(current_path, "statics"),
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
)

2. 模板语法

2-1 变量与表达式

在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>{{price}}</span>/晚</div>
        <div class="house-intro">
            <span class="house-title">{{title}}</span>
            <em>整套出租 - {{score}}分/{{comments}}点评 - {{position}}</em>
        </div>
    </div>
</li>

渲染方式如下:

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

{{}}不仅可以包含变量,还可以是表达式,如:

<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)

2-2 控制语句

可以在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>

写入的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 %}

4.3 练习

  1. 对比Django模板与Tornado模板的异同。
  2. 练习使用Tornado模板的语法。

4.3 练习

  1. 对比Django模板与Tornado模板的异同。
  2. 练习使用Tornado模板的语法。

原文地址:https://www.cnblogs.com/alexzhang92/p/9379928.html

时间: 2024-08-14 14:39:49

Tornado之模板的相关文章

tornado之模板扩展

当我们有多个模板的时候,很多模板之间其实相似度很高.我们期望可以重用部分网页代码.这在tornado中可以通过extends语句来实现.为了扩展一个已经存在的模板,你只需要在新的模板文件的顶部放上一句{% extends "filename.html" %}.比如,为了在新模板中扩展一个父模板(在这里假设为之前我们使用过的index.html),你可以这样使用:{% extends "index.html" %} 这就使得新文件继承index.html的所有标签,并

python SSTI tornado render模板注入

原理tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式.简单的理解例子如下:------------------------------------------------------------------------------------import tornado.ioloopimport tornado.web cla

2018护网杯easy_tornado(SSTI tornado render模板注入)

考点:SSTI注入 原理: tornado render是python中的一个渲染函数,也就是一种模板,通过调用的参数不同,生成不同的网页,如果用户对render内容可控,不仅可以注入XSS代码,而且还可以通过{{}}进行传递变量和执行简单的表达式. 网上看到的例子: #!/usr/bin/env python # -*- coding:utf-8 -*- from tornado.web import UIModule from tornado import escape class cust

Tornado小记 -- 模板中的Handler

在Tornado的前端页面模板中,Tornado提供了一些对象别名来快速访问对象,具体定义可以参考Tornado官方文档! 这里我想将的是Handler这个对象,Handler指向的处理当前这个页面的RequestHandler对象!但我在Tornado的Blog Demo中,发现了这样的语句: 1 <title> 2 {{ escape(handler.settings["blog_title"]) }} 3 </title> 但是奇怪的是RequestHan

tornado模板源码小析

最近对flask的热情有点下降,对tornado有点高涨. 之前在知乎上回答过一个问题,如何理解 Tornado ?,我的回答如下: 1.高性能的网络库,这可以和gevent,twisted,libevent等做对. 提供了异步io支持,超时事件处理,在此基础上提供了tcpserver,httpclient,尤其是curlhttpclient, 在现有http客户端中肯定排第一.可以用来做爬虫,游戏服务器,据我所知业界已有使用tornado作为游戏服务器 2.web框架,这可以和django,f

tornado模板引擎原理

前言 老师问小明:已经a=1, 求a+1的值. 小明挠挠头,思考后回答:老师,a+1的结果是2. 以上是一个非常简单的例子,实际上就是一个模板编译过程. a=1,表示一个命名空间(namespace)中的一项,该命名空间中存在一个名称(name)为a,其值为1.在python里面,命名空间可以用数据结构字典来表示. a+1,实际上是一个非常简单的模板.这个模板里面有且只有一条规则a+1.本博文中所说的模板,实际上更类似一份空白的数学考卷.这份数学考卷中,有很多类似上面老师问小明的题目. 学生解决

tornado之表单和模板

之前在indexHandler中通过self.write()方法在对应的网页中写入具体的字符信息. 如果我们想直接返回一个网页那么这个时候就需要用到模板了 首先在工程目录下新建一个template文件夹.然后在Application中引用该文件夹的地址.template_path参数告诉Tornado在哪里寻找模板文件.下面的代码告诉Python在你Tornado应用文件同目录下的templates文件夹中寻找模板文件. app = tornado.web.Application(handler

Python Tornado初学笔记之表单与模板(一)

Tornado中的表单和HTML5中的表单具有相同的用途,同样是用于内容的填写.只是不同的是Tornado中的表单需要传入到后台,然后通过后台进行对模板填充. 模板:是一个允许嵌入Python代码片段的HTML文件. 一.简单模板示例: Python主程序: import os.path import tornado.httpserver import tornado.ioloop import tornado.options import tornado.web from tornado.op

Tornado模板

--------------------静态文件--------------------1.static_path:通过向web.Application类的构造函数传递一个名为static_path的参数来告诉Tornado从文件系统的一个特定位置提供静态文件app = tornado.web.Application([(r'/', IndexHandler)],static_path=os.path.join(os.path.dirname(file), "statics"),) 2