(2018-05-27.Python從Zero到One)8、(Tornado)模板tornado__1.4.2使用模板

4.2 使用模板

1. 路徑與渲染

使用模板,需要仿照靜態(tài)文件路徑設(shè)置一樣,向web.Application類的構(gòu)造函數(shù)傳遞一個(gè)名為template_path的參數(shù)來(lái)告訴Tornado從文件系統(tǒng)的一個(gè)特定位置提供模板文件,如:

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

在這里,我們?cè)O(shè)置了一個(gè)當(dāng)前應(yīng)用目錄下名為templates的子目錄作為template_path的參數(shù)。在handler中使用的模板將在此目錄中尋找。

現(xiàn)在我們將靜態(tài)文件目錄statics/html中的index.html復(fù)制一份到templates目錄中,此時(shí)文件目錄結(jié)構(gòu)為:

.
├── 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()方法來(lái)渲染模板并返回給客戶端。

class IndexHandler(RequestHandler):
    def get(self):
        self.render("index.html") # 渲染主頁(yè)模板,并返回給客戶端。

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. 模板語(yǔ)法

2-1 變量與表達(dá)式

在tornado的模板中使用{{}}作為變量或表達(dá)式的占位符,使用render渲染后占位符{{}}會(huì)被替換為相應(yīng)的結(jié)果值。

我們將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平大空間+文化保護(hù)區(qū)雙地鐵</span>
            <em>整套出租 - 5分/6點(diǎn)評(píng) - 北京市豐臺(tái)區(qū)六里橋地鐵</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}}點(diǎn)評(píng) - {{position}}</em>
        </div>
    </div>
</li>

渲染方式如下:

class IndexHandler(RequestHandler):
    def get(self):
        house_info = {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        }
        self.render("index.html", **house_info)

{{}}不僅可以包含變量,還可以是表達(dá)式,如:

<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}}點(diǎn)評(píng) - {{position}}</em>
        </div>
    </div>
</li>

class IndexHandler(RequestHandler):
    def get(self):
        house_info = {
            "p1": 198,
            "p2": 200,
            "titles": ["寬窄巷子", "160平大空間", "文化保護(hù)區(qū)雙地鐵"],
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        }
        self.render("index.html", **house_info)

2-2 控制語(yǔ)句

可以在Tornado模板中使用Python條件和循環(huán)語(yǔ)句??刂普Z(yǔ)句以{%和%}包圍,并以類似下面的形式被使用:

{% if page is None %}

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

控制語(yǔ)句的大部分就像對(duì)應(yīng)的Python語(yǔ)句一樣工作,支持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"]}}點(diǎn)評(píng) - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        對(duì)不起,暫時(shí)沒有房源。
    {% end %}
</ul>

python中渲染語(yǔ)句為:

class IndexHandler(RequestHandler):
    def get(self):
        houses = [
        {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        },
        {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        },
        {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        },
        {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        },
        {
            "price": 398,
            "title": "寬窄巷子+160平大空間+文化保護(hù)區(qū)雙地鐵",
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        }]
        self.render("index.html", houses=houses)

2-3 函數(shù)

static_url()

Tornado模板模塊提供了一個(gè)叫作static_url的函數(shù)來(lái)生成靜態(tài)文件目錄下文件的URL。如下面的示例代碼:

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

這個(gè)對(duì)static_url的調(diào)用生成了URL的值,并渲染輸出類似下面的代碼:

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

優(yōu)點(diǎn):

  • static_url函數(shù)創(chuàng)建了一個(gè)基于文件內(nèi)容的hash值,并將其添加到URL末尾(查詢字符串的參數(shù)v)。這個(gè)hash值確保瀏覽器總是加載一個(gè)文件的最新版而不是之前的緩存版本。無(wú)論是在你應(yīng)用的開發(fā)階段,還是在部署到生產(chǎn)環(huán)境使用時(shí),都非常有用,因?yàn)槟愕挠脩舨槐卦贋榱丝吹侥愕撵o態(tài)內(nèi)容而清除瀏覽器緩存了。
  • 另一個(gè)好處是你可以改變你應(yīng)用URL的結(jié)構(gòu),而不需要改變模板中的代碼。例如,可以通過(guò)設(shè)置static_url_prefix來(lái)更改Tornado的默認(rèn)靜態(tài)路徑前綴/static。如果使用static_url而不是硬編碼的話,代碼不需要改變。

轉(zhuǎn)義

我們新建一個(gè)表單頁(yè)面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>

對(duì)應(yīng)的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)

當(dāng)我們?cè)诒韱沃刑钊肴缦聝?nèi)容時(shí):

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

day61_tornado-模板-01.png

寫入的js程序并沒有運(yùn)行,而是顯示出來(lái)了:

day61_tornado-模板-02.png

我們查看頁(yè)面源代碼,發(fā)現(xiàn)<、>、"等被轉(zhuǎn)換為對(duì)應(yīng)的html字符。

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

這是因?yàn)閠ornado中默認(rèn)開啟了模板自動(dòng)轉(zhuǎn)義功能,防止網(wǎng)站受到惡意攻擊。

我們可以通過(guò)raw語(yǔ)句來(lái)輸出不被轉(zhuǎn)義的原始格式,如:

{% raw text %}

注意:在Firefox瀏覽器中會(huì)直接彈出alert窗口,而在Chrome瀏覽器中,需要set_header("X-XSS-Protection", 0)

若要關(guān)閉自動(dòng)轉(zhuǎn)義,一種方法是在Application構(gòu)造函數(shù)中傳遞autoescape=None,另一種方法是在每頁(yè)模板中修改自動(dòng)轉(zhuǎn)義行為,添加如下語(yǔ)句:

{% autoescape None %}

escape()

關(guān)閉自動(dòng)轉(zhuǎn)義后,可以使用escape()函數(shù)來(lái)對(duì)特定變量進(jìn)行轉(zhuǎn)義,如:

{{ escape(text) }}

自定義函數(shù)

在模板中還可以使用一個(gè)自己編寫的函數(shù),只需要將函數(shù)名作為模板的參數(shù)傳遞即可,就像其他變量一樣。

我們修改后端如下:

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

class IndexHandler(RequestHandler):
    def get(self):
        house_list = [
        {
            "price": 398,
            "titles": ["寬窄巷子", "160平大空間", "文化保護(hù)區(qū)雙地鐵"],
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        },
        {
            "price": 398,
            "titles": ["寬窄巷子", "160平大空間", "文化保護(hù)區(qū)雙地鐵"],
            "score": 5,
            "comments": 6,
            "position": "北京市豐臺(tái)區(qū)六里橋地鐵"
        }]
        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"]}}點(diǎn)評(píng) - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        對(duì)不起,暫時(shí)沒有房源。
    {% end %}
</ul>

2-4 塊

我們可以使用塊來(lái)復(fù)用模板,塊語(yǔ)法如下:

{% block block_name %} {% end %}

現(xiàn)在,我們對(duì)模板index.html進(jìn)行抽象,抽離出父模板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來(lái)使用父模板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"]}}點(diǎn)評(píng) - {{house["position"]}}</em>
                </div>
            </div>
        </li>
        {% end %}
    {% else %}
        對(duì)不起,暫時(shí)沒有房源。
    {% end %}
    </ul>
{% end %}

{% block footer %}
    <p><span><i class="fa fa-copyright"></i></span>愛家租房&nbsp;&nbsp;享受家的溫馨</p>
{% end %}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容