鍍金池/ 教程/ Python/ 用 tornado 做網(wǎng)站 (4)
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

用 tornado 做網(wǎng)站 (4)

模板

已經(jīng)基本了解前端向和后端如何傳遞數(shù)據(jù),以及后端如何接收數(shù)據(jù)的過程和方法之后。我突然發(fā)現(xiàn),前端頁面寫的太難看了。俗話說“外行看熱鬧,內(nèi)行看門道”。程序員寫的網(wǎng)站,在更多時候是給“外行”看的,他們可沒有耐心來看代碼,他們看的就是界面,因此界面是否做的漂亮一點點,是直觀重要的。

其實,也不僅僅是漂亮的原因,因為前端頁面,還要顯示從后端讀取出來的數(shù)據(jù)呢。

恰好,tornado 提供比較好用的前端模板(tornado.template)。通過這個模板,能夠讓前端編寫更方便。

render()

render() 方法能夠告訴 tornado 讀入哪個模板,插入其中的模板代碼,并返回結(jié)果給瀏覽器。比如在 IndexHandler 類中 get() 方法里面的 self.render("index.html"),就是讓 tornado 到 templates 目中找到名為 index.html 的文件,讀出它的內(nèi)容,返回給瀏覽器。這樣用戶就能看到 index.html 所規(guī)定的頁面了。當然,在前面所寫的 index.html 還僅僅是 html 標記,沒有顯示出所謂“模板”的作用。為此,將 index.html 和 index.py 文件做如下改造。

#!/usr/bin/env Python
# coding=utf-8

import tornado.web
import methods.readdb as mrd

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        usernames = mrd.select_columns(table="users",column="username")
        one_user = usernames[0][0]
        self.render("index.html", user=one_user)

index.py 文件中,只修改了 get() 方法,從數(shù)據(jù)庫中讀取用戶名,并且提出一個用戶(one_user),然后通過 self.render("index.html", user=one_user) 將這個用戶名放到 index.html 中,其中 user=one_user 的作用就是傳遞對象到模板。

提醒讀者注意的是,在上面的代碼中,我使用了 mrd.select_columns(table="users",column="username"),也就是說必須要在 methods 目錄中的 readdb.py 文件中有一個名為 select_columns 的函數(shù)。為了使讀者能夠理解,貼出已經(jīng)修改之后的 readdb.py 文件代碼,比上一節(jié)多了函數(shù) select_columns:

#!/usr/bin/env Python
# coding=utf-8

from db import *

def select_table(table, column, condition, value ):
    sql = "select " + column + " from " + table + " where " + condition + "='" + value + "'"
    cur.execute(sql)
    lines = cur.fetchall()
    return lines

def select_columns(table, column ):
    sql = "select " + column + " from " + table
    cur.execute(sql)
    lines = cur.fetchall()
    return lines

下面是 index.html 修改后的代碼:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Learning Python</title>
</head>
<body>
    <h2> 登錄頁面</h2>
    <p>用用戶名為:{{user}}登錄</p>
    <form method="POST">
        <p><span>UserName:</span><input type="text" id="username"/></p>
        <p><span>Password:</span><input type="password" id="password" /></p>
        <p><input type="BUTTON" value="登錄" id="login" /></p>
    </form>
    <script src="{{static_url("js/jquery.min.js")}}"></script>
    <script src="{{static_url("js/script.js")}}"></script>
</body>

<p> 用用戶名為:{{user}}登錄</p>,這里用了{{ }}方式,接受對應的變量引導來的對象。也就是在首頁打開之后,用戶應當看到有一行提示。如下圖一樣。

http://wiki.jikexueyuan.com/project/start-learning-python/images/30601.png" alt="" />

圖中箭頭是我為了強調(diào)后來加上去的,箭頭所指的,就是從數(shù)據(jù)庫中讀取出來的用戶名,借助于模板中的雙大括號{{ }}顯示出來。

{{ }}本質(zhì)上是占位符。當這個 html 被執(zhí)行的時候,這個位置會被一個具體的對象(例如上面就是字符串 qiwsir)所替代。具體是哪個具體對象替代這個占位符,完全是由 render() 方法中關鍵詞來指定,也就是 render() 中的關鍵詞與模板中的占位符包裹著的關鍵詞一致。

用這種方式,修改一下用戶正確登錄之后的效果。要求用戶正確登錄之后,跳轉(zhuǎn)到另外一個頁面,并且在那個頁面中顯示出用戶的完整信息。

先修改 url.py 文件,在其中增加一些內(nèi)容。完整代碼如下:

#!/usr/bin/env Python
# coding=utf-8
"""
the url structure of website
"""
import sys
reload(sys)
sys.setdefaultencoding("utf-8")

from handlers.index import IndexHandler
from handlers.user import UserHandler

url = [
    (r'/', IndexHandler),
    (r'/user', UserHandler),
]

然后就建立 handlers/user.py 文件,內(nèi)容如下:

#!/usr/bin/env Python
# coding=utf-8

import tornado.web
import methods.readdb as mrd

class UserHandler(tornado.web.RequestHandler):
    def get(self):
        username = self.get_argument("user")
        user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
        self.render("user.html", users = user_infos)

在 get() 中使用 self.get_argument("user"),目的是要通過 url 獲取參數(shù) user 的值。因此,當用戶登錄后,得到正確返回值,那么 js 應該用這樣的方式載入新的頁面。

注意:上述的 user.py 代碼為了簡單突出本將要說明的,沒有對 user_infos 的結(jié)果進行判斷。在實際的編程中,這要進行判斷或者使用 try...except。

$(document).ready(function(){
    $("#login").click(function(){
        var user = $("#username").val();
        var pwd = $("#password").val();
        var pd = {"username":user, "password":pwd};
        $.ajax({
            type:"post",
            url:"/",
            data:pd,
            cache:false,
            success:function(data){
                window.location.href = "/user?user="+data;
            },
            error:function(){
                alert("error!");
            },
        });
    });
});

接下來是 user.html 模板。注意上面的代碼中,user_infos 引用的對象不是一個字符串了,也就是傳入模板的不是一個字符串,是一個元組。對此,模板這樣來處理它。

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Learning Python</title>
</head>
<body>
    <h2>Your informations are:</h2>
    <ul>
        {% for one in users %}
            <li>username:{{one[1]}}</li>
            <li>password:{{one[2]}}</li>
            <li>email:{{one[3]}}</li>
        {% end %}
    </ul>
</body>

顯示的效果是:

http://wiki.jikexueyuan.com/project/start-learning-python/images/30602.png" alt="" />

在上面的模板中,其實用到了模板語法。

模板語法

在模板的雙大括號中,可以寫類似 Python 的語句或者表達式。比如:

>>> from tornado.template import Template
>>> print Template("{{ 3+4 }}").generate()
7
>>> print Template("{{ 'python'[0:2] }}").generate()
py
>>> print Template("{{ '-'.join(str(i) for i in range(10)) }}").generate()
0-1-2-3-4-5-6-7-8-9

意即如果在模板中,某個地方寫上{{ 3+4 }},當那個模板被 render() 讀入之后,在頁面上該占位符的地方就顯示 7。這說明 tornado 自動將雙大括號內(nèi)的表達式進行計算,并將其結(jié)果以字符串的形式返回到瀏覽器輸出。

除了表達式之外,Python 的語句也可以在表達式中使用,包括 if、for、while 和 try。只不過要有一個語句開始和結(jié)束的標記,用以區(qū)分那里是語句、哪里是 HTML 標記符。

語句的形式:{{% 語句 %}}

例如:

{{% if user=='qiwsir' %}}
    {{ user }}
{{% end %}}

上面的舉例中,第一行雖然是 if 語句,但是不要在后面寫冒號了。最后一行一定不能缺少,表示語句塊結(jié)束。將這一個語句塊放到模板中,當被 render 讀取此模板的時候,tornado 將執(zhí)行結(jié)果返回給瀏覽器顯示,跟前面的表達式一樣。實際的例子可以看上圖輸出結(jié)果和對應的循環(huán)語句。

轉(zhuǎn)義字符

雖然讀者現(xiàn)在已經(jīng)對字符轉(zhuǎn)義問題不陌生了,但是在網(wǎng)站開發(fā)中,它還將是一個令人感到麻煩的問題。所謂轉(zhuǎn)義字符(Escape Sequence)也稱字符實體(Character Entity),它的存在是因為在網(wǎng)頁中 <, > 之類的符號,是不能直接被輸出的,因為它們已經(jīng)被用作了 HTML 標記符了,如果在網(wǎng)頁上用到它們,就要轉(zhuǎn)義。另外,也有一些字符在 ASCII 字符集中沒有定義(如版權(quán)符號“?”),這樣的符號要在 HTML 中出現(xiàn),也需要轉(zhuǎn)義字符(如“?”對應的轉(zhuǎn)義字符是“&copy;”)。

上述是指前端頁面的字符轉(zhuǎn)義,其實不僅前端,在后端程序中,因為要讀寫數(shù)據(jù)庫,也會遇到字符轉(zhuǎn)義問題。

比如一個簡單的查詢語句:select username, password from usertable where username='qiwsir',如果在登錄框中沒有輸入 qiwsir,而是輸入了 a;drop database;,這個查詢語句就變成了 select username, password from usertable where username=a; drop database;,如果后端程序執(zhí)行了這條語句會怎么樣呢?后果很嚴重,因為會 drop database,屆時真的是欲哭無淚了。類似的情況還很多,比如還可以輸入 <input type="text" />,結(jié)果出現(xiàn)了一個輸入框,如果是 <form action="...",會造成跨站攻擊了。這方面的問題還不少呢,讀者有空可以到網(wǎng)上搜一下所謂 sql 注入問題,能了解更多。

所以,后端也要轉(zhuǎn)義。

轉(zhuǎn)義是不是很麻煩呢?

Tornado 為你著想了,因為存在以上轉(zhuǎn)義問題,而且會有粗心的程序員忘記了,于是 Tornado 中,模板默認為自動轉(zhuǎn)義。這是多么好的設計呀。于是所有表單輸入的,你就不用擔心會遇到上述問題了。

為了能夠體會自動轉(zhuǎn)義,不妨在登錄框中輸入上面那樣字符,然后可以用 print 語句看一看,后臺得到了什么。

print 語句,在 Python3 中是 print() 函數(shù),在進行程序調(diào)試的時候非常有用。經(jīng)常用它把要看個究竟的東西打印出來。

自動轉(zhuǎn)義是一個好事情,但是,有時候會不需要轉(zhuǎn)義,比如想在模板中這樣做:

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Learning Python</title>
</head>
<body>
    <h2>登錄頁面</h2>
    <p>用用戶名為:{{user}}登錄</p>
    <form method="POST">
        <p><span>UserName:</span><input type="text" id="username"/></p>
        <p><span>Password:</span><input type="password" id="password" /></p>
        <p><input type="BUTTON" value="登錄" id="login" /></p>
    </form>
    {% set website = "<a >welcome to my website</a>" %}
    {{ website }}
    <script src="{{static_url("js/jquery.min.js")}}"></script>
    <script src="{{static_url("js/script.js")}}"></script>
</body>

這是 index.html 的代碼,我增加了 {% set website = "<a >welcome to my website</a>" %},作用是設置一個變量,名字是 website,它對應的內(nèi)容是一個做了超鏈接的文字。然后在下面使用這個變量{{ website }},本希望能夠出現(xiàn)的是有一行字“welcome to my website”,點擊這行字,就可以打開對應鏈接的網(wǎng)站??墒?,看到了這個:

http://wiki.jikexueyuan.com/project/start-learning-python/images/30603.png" alt="" />

下面那一行,把整個源碼都顯示出來了。這就是因為自動轉(zhuǎn)義的結(jié)果。這里需要的是不轉(zhuǎn)義。于是可以將{{ website }}修改為:

{% raw website %}

表示這一行不轉(zhuǎn)義。但是別的地方還是轉(zhuǎn)義的。這是一種最推薦的方法。

http://wiki.jikexueyuan.com/project/start-learning-python/images/30604.png" alt="" />

如果你要全轉(zhuǎn)義,可以使用:

{% autoescape None %}
{{ website }}

貌似省事,但是我不推薦。

幾個備查函數(shù)

下面幾個函數(shù),放在這里備查,或許在某些時候用到。都是可以使用在模板中的。

  • escape(s):替換字符串 s 中的 &、<、> 為他們對應的 HTML 字符。
  • url_escape(s):使用 urllib.quote_plus 替換字符串 s 中的字符為 URL 編碼形式。
  • json_encode(val):將 val 編碼成 JSON 格式。
  • squeeze(s):過濾字符串 s,把連續(xù)的多個空白字符替換成一個空格。

此外,在模板中也可以使用自己編寫的函數(shù)。但不常用。所以本教程就不啰嗦這個了。


總目錄   |   上節(jié):用 tornado 做網(wǎng)站 (3)   |   下節(jié):用 tornado 做網(wǎng)站 (5)

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。

上一篇:語句(2)下一篇:為計算做準備