已經基本了解前端向和后端如何傳遞數(shù)據,以及后端如何接收數(shù)據的過程和方法之后。我突然發(fā)現(xiàn),前端頁面寫的太難看了。俗話說“外行看熱鬧,內行看門道”。程序員寫的網站,在更多時候是給“外行”看的,他們可沒有耐心來看代碼,他們看的就是界面,因此界面是否做的漂亮一點點,是直觀重要的。
其實,也不僅僅是漂亮的原因,因為前端頁面,還要顯示從后端讀取出來的數(shù)據呢。
恰好,tornado提供比較好用的前端模板(tornado.template)。通過這個模板,能夠讓前端編寫更方便。
render()方法能夠告訴tornado讀入哪個模板,插入其中的模板代碼,并返回結果給瀏覽器。比如在IndexHandler類中get()方法里面的self.render("index.html")
,就是讓tornado到templates目中找到名為index.html的文件,讀出它的內容,返回給瀏覽器。這樣用戶就能看到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ù)據庫中讀取用戶名,并且提出一個用戶(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ù)。為了使讀者能夠理解,貼出已經修改之后的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="https://atts.w3cschool.cn/attachments/image/cimg/jquery.min.js")}}"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/script.js")}}"></script>
</body>
<p>用用戶名為:{{user}}登錄</p>
,這里用了{{ }}
方式,接受對應的變量引導來的對象。也就是在首頁打開之后,用戶應當看到有一行提示。如下圖一樣。
圖中箭頭是我為了強調后來加上去的,箭頭所指的,就是從數(shù)據庫中讀取出來的用戶名,借助于模板中的雙大括號{{ }}
顯示出來。
{{ }}
本質上是占位符。當這個html被執(zhí)行的時候,這個位置會被一個具體的對象(例如上面就是字符串qiwsir)所替代。具體是哪個具體對象替代這個占位符,完全是由render()方法中關鍵詞來指定,也就是render()中的關鍵詞與模板中的占位符包裹著的關鍵詞一致。
用這種方式,修改一下用戶正確登錄之后的效果。要求用戶正確登錄之后,跳轉到另外一個頁面,并且在那個頁面中顯示出用戶的完整信息。
先修改url.py文件,在其中增加一些內容。完整代碼如下:
#!/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文件,內容如下:
#!/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的結果進行判斷。在實際的編程中,這要進行判斷或者使用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>
顯示的效果是:
在上面的模板中,其實用到了模板語法。
在模板的雙大括號中,可以寫類似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自動將雙大括號內的表達式進行計算,并將其結果以字符串的形式返回到瀏覽器輸出。
除了表達式之外,python的語句也可以在表達式中使用,包括if、for、while和try。只不過要有一個語句開始和結束的標記,用以區(qū)分那里是語句、哪里是HTML標記符。
語句的形式:{{% 語句 %}}
例如:
{{% if user=='qiwsir' %}}
{{ user }}
{{% end %}}
上面的舉例中,第一行雖然是if語句,但是不要在后面寫冒號了。最后一行一定不能缺少,表示語句塊結束。將這一個語句塊放到模板中,當被render讀取此模板的時候,tornado將執(zhí)行結果返回給瀏覽器顯示,跟前面的表達式一樣。實際的例子可以看上圖輸出結果和對應的循環(huán)語句。
雖然讀者現(xiàn)在已經對字符轉義問題不陌生了,但是在網站開發(fā)中,它還將是一個令人感到麻煩的問題。所謂轉義字符(Escape Sequence)也稱字符實體(Character Entity),它的存在是因為在網頁中<, >
之類的符號,是不能直接被輸出的,因為它們已經被用作了HTML標記符了,如果在網頁上用到它們,就要轉義。另外,也有一些字符在ASCII字符集中沒有定義(如版權符號“?”),這樣的符號要在HTML中出現(xiàn),也需要轉義字符(如“?”對應的轉義字符是“&copy;”)。
上述是指前端頁面的字符轉義,其實不僅前端,在后端程序中,因為要讀寫數(shù)據庫,也會遇到字符轉義問題。
比如一個簡單的查詢語句: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" />
,結果出現(xiàn)了一個輸入框,如果是<form action="..."
,會造成跨站攻擊了。這方面的問題還不少呢,讀者有空可以到網上搜一下所謂sql注入問題,能了解更多。
所以,后端也要轉義。
轉義是不是很麻煩呢?
Tornado為你著想了,因為存在以上轉義問題,而且會有粗心的程序員忘記了,于是Tornado中,模板默認為自動轉義。這是多么好的設計呀。于是所有表單輸入的,你就不用擔心會遇到上述問題了。
為了能夠體會自動轉義,不妨在登錄框中輸入上面那樣字符,然后可以用print語句看一看,后臺得到了什么。
print語句,在python3中是print()函數(shù),在進行程序調試的時候非常有用。經常用它把要看個究竟的東西打印出來。
自動轉義是一個好事情,但是,有時候會不需要轉義,比如想在模板中這樣做:
<!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="https://atts.w3cschool.cn/attachments/image/cimg/jquery.min.js")}}"></script>
<script src="https://atts.w3cschool.cn/attachments/image/cimg/script.js")}}"></script>
</body>
這是index.html的代碼,我增加了{% set website = "<a >welcome to my website</a>" %}
,作用是設置一個變量,名字是website,它對應的內容是一個做了超鏈接的文字。然后在下面使用這個變量{{ website }}
,本希望能夠出現(xiàn)的是有一行字“welcome to my website”,點擊這行字,就可以打開對應鏈接的網站??墒?,看到了這個:
下面那一行,把整個源碼都顯示出來了。這就是因為自動轉義的結果。這里需要的是不轉義。于是可以將{{ website }}
修改為:
{% raw website %}
表示這一行不轉義。但是別的地方還是轉義的。這是一種最推薦的方法。
如果你要全轉義,可以使用:
{% autoescape None %}
{{ website }}
貌似省事,但是我不推薦。
下面幾個函數(shù),放在這里備查,或許在某些時候用到。都是可以使用在模板中的。
此外,在模板中也可以使用自己編寫的函數(shù)。但不常用。所以本教程就不啰嗦這個了。
更多建議: