Skip to content

Commit a2c33bc

Browse files
committed
tornado 4
1 parent 365c570 commit a2c33bc

20 files changed

Lines changed: 287 additions & 5 deletions

File tree

2code/pm.pyc

319 Bytes
Binary file not shown.

306.md

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
>我的弟兄们,你们落在百般试炼中,都要以为大喜乐;因为你们的信心经过试验,就生忍耐。但忍耐也当成功,使你们成全完备,毫无缺欠。你们中间若有缺少智慧的,应当求那厚赐与众人、也不斥责人的神,主就必赐给他。(JAMES 1:2-5)
2+
3+
#同tornado做网站(4)
4+
5+
##模板
6+
7+
已经基本了解前端向后端如何传递数据,以及后端如何接收数据的过程和方法之后。我突然发现,前端页面写的太难看了。俗话说“外行看热闹,内行看门道”。程序员写的网站,在更多时候是给“外行”看的,他们可没有耐心来看代码,他们看的就是界面,因此界面是否做的漂亮一点点,是直观重要的。
8+
9+
其实,也不仅仅是漂亮的原因,因为前端页面,还要显示从后端读取出来的数据呢。
10+
11+
恰好,tornado提供比较好用的前端模板(tornado.template)。通过这个模板,能够让前端编写更方便。
12+
13+
###render()
14+
15+
render()方法能够告诉tornado读入哪个模板,插入其中的模板代码,并返回结果给浏览器。比如在IndexHandler类中get()方法里面的`self.render("index.html")`,就是让tornado到templates目中找到名为index.html的文件,读出它的内容,返回给浏览器。这样用户就能看到index.html所规定的页面了。当然,在前面所写的index.html还仅仅是html标记,没有显示出所谓“模板”的作用。为此,将index.html和index.py文件做如下改造。
16+
17+
#!/usr/bin/env python
18+
# coding=utf-8
19+
20+
import tornado.web
21+
import methods.readdb as mrd
22+
23+
class IndexHandler(tornado.web.RequestHandler):
24+
def get(self):
25+
usernames = mrd.select_columns(table="users",column="username")
26+
one_user = usernames[0][0]
27+
self.render("index.html", user=one_user)
28+
29+
index.py文件中,只修改了get()方法,从数据库中读取用户名,并且提出一个用户(one_user),然后通过`self.render("index.html", user=one_user)`将这个用户名放到index.html中,其中`user=one_user`的作用就是传递对象到模板。index.html的代码修改为:
30+
31+
<!DOCTYPE html>
32+
<head>
33+
<meta charset="UTF-8">
34+
<meta name="viewport" content="width=device-width, initial-scale=1" />
35+
<title>Learning Python</title>
36+
</head>
37+
<body>
38+
<h2>登录页面</h2>
39+
<p>用用户名为:{{user}}登录</p>
40+
<form method="POST">
41+
<p><span>UserName:</span><input type="text" id="username"/></p>
42+
<p><span>Password:</span><input type="password" id="password" /></p>
43+
<p><input type="BUTTON" value="登录" id="login" /></p>
44+
</form>
45+
<script src="{{static_url("js/jquery.min.js")}}"></script>
46+
<script src="{{static_url("js/script.js")}}"></script>
47+
</body>
48+
49+
`<p>用用户名为:{{user}}登录</p>`,这里用了`{{ }}`方式,接受对应的变量引导来的对象。也就是在首页打开之后,用户应当看到有一行提示。如下图一样。
50+
51+
![](./3images/30601.png)
52+
53+
图中箭头是我为了强调后来加上去的,箭头所指的,就是从数据库中读取出来的用户名,借助于模板中的双大括号`{{ }}`显示出来。
54+
55+
`{{ }}`本质上是占位符。当这个html被执行的时候,这个位置会被一个具体的对象(例如上面就是字符串qiwsir)所替代。具体是哪个具体对象替代这个占位符,完全是由render()方法中关键词来指定,也就是render()中的关键词与模板中的占位符包裹着的关键词一致。
56+
57+
用这种方式,修改一下用户正确登录之后的效果。要求用户正确登录之后,跳转到另外一个页面,并且在那个页面中显示出用户的完整信息。
58+
59+
先修改url.py文件,在其中增加一些内容。完整代码如下:
60+
61+
#!/usr/bin/env python
62+
# coding=utf-8
63+
"""
64+
the url structure of website
65+
"""
66+
import sys
67+
reload(sys)
68+
sys.setdefaultencoding("utf-8")
69+
70+
from handlers.index import IndexHandler
71+
from handlers.user import UserHandler
72+
73+
url = [
74+
(r'/', IndexHandler),
75+
(r'/user', UserHandler),
76+
]
77+
78+
然后就建立handlers/user.py文件,内容如下:
79+
80+
#!/usr/bin/env python
81+
# coding=utf-8
82+
83+
import tornado.web
84+
import methods.readdb as mrd
85+
86+
class UserHandler(tornado.web.RequestHandler):
87+
def get(self):
88+
username = self.get_argument("user")
89+
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
90+
self.render("user.html", users = user_infos)
91+
92+
在get()中使用`self.get_argument("user")`,目的是要通过url获取参数user的值。因此,当用户登录后,得到正确返回值,那么js应该用这样的方式载入新的页面。
93+
94+
注意:上述的user.py代码为了简单突出本将要说明的,没有对user_infos的结果进行判断。在实际的编程中,这要进行判断或者使用try...except。
95+
96+
$(document).ready(function(){
97+
$("#login").click(function(){
98+
var user = $("#username").val();
99+
var pwd = $("#password").val();
100+
var pd = {"username":user, "password":pwd};
101+
$.ajax({
102+
type:"post",
103+
url:"/",
104+
data:pd,
105+
cache:false,
106+
success:function(data){
107+
window.location.href = "/user?user="+data;
108+
},
109+
error:function(){
110+
alert("error!");
111+
},
112+
});
113+
});
114+
});
115+
116+
接下来是user.html模板。注意上面的代码中,user_infos引用的对象不是一个字符串了,也就是传入模板的不是一个字符串,是一个元组。对此,模板这样来处理它。
117+
118+
<!DOCTYPE html>
119+
<head>
120+
<meta charset="UTF-8">
121+
<meta name="viewport" content="width=device-width, initial-scale=1" />
122+
<title>Learning Python</title>
123+
</head>
124+
<body>
125+
<h2>Your informations are:</h2>
126+
<ul>
127+
{% for one in users %}
128+
<li>username:{{one[1]}}</li>
129+
<li>password:{{one[2]}}</li>
130+
<li>email:{{one[3]}}</li>
131+
{% end %}
132+
</ul>
133+
</body>
134+
135+
显示的效果是:
136+
137+
![](./3images/30602.png)
138+
139+
在上面的模板中,其实用到了模板语法。
140+
141+
###模板语法
142+
143+
在模板的双大括号中,可以写类似python的语句或者表达式。比如:
144+
145+
>>> from tornado.template import Template
146+
>>> print Template("{{ 3+4 }}").generate()
147+
7
148+
>>> print Template("{{ 'python'[0:2] }}").generate()
149+
py
150+
>>> print Template("{{ '-'.join(str(i) for i in range(10)) }}").generate()
151+
0-1-2-3-4-5-6-7-8-9
152+
153+
意即如果在模板中,某个地方写上`{{ 3+4 }}`,当那个模板被render()读入之后,在页面上该占位符的地方就显示`7`。这说明tornado自动将双大括号内的表达式进行计算,并将其结果以字符串的形式返回到浏览器输出。
154+
155+
除了表达式之外,python的语句也可以在表达式中使用,包括if、for、while和try。只不过要有一个语句开始和结束的标记,用以区分那里是语句、哪里是HTML标记符。
156+
157+
语句的形式:`{{% 语句 %}}`
158+
159+
例如:
160+
161+
{{% if user=='qiwsir' %}}
162+
{{ user }}
163+
{{% end %}}
164+
165+
上面的举例中,第一行虽然是if语句,但是不要在后面写冒号了。最后一行一定不能缺少,表示语句块结束。将这一个语句块放到模板中,当被render读取此模板的时候,tornado将执行结果返回给浏览器显示,跟前面的表达式一样。实际的例子可以看上图输出结果和对应的循环语句。
166+
167+
##转义字符
168+
169+
虽然读者现在已经对字符转义问题不陌生了,但是在网站开发中,它还将是一个令人感到麻烦的问题。所谓转义字符(Escape Sequence)也称字符实体(Character Entity),它的存在是因为在网页中`<, >`之类的符号,是不能直接被输出的,因为它们已经被用作了HTML标记符了,如果在网页上用到它们,就要转义。另外,也有一些字符在ASCII字符集中没有定义(如版权符号“©”),这样的符号要在HTML中出现,也需要转义字符(如“©”对应的转义字符是“&copy;”)。
170+
171+
上述是指前端页面的字符转义,其实不仅前端,在后端程序中,因为要读写数据库,也会遇到字符转义问题。
172+
173+
比如一个简单的查询语句:`select username, password from usertable where username='qiwsir'`,如果在登录框中没有输入qiwsir,而是输入了`a;drop database;`,这个查询语句就变成了`select username, password from usertable where username=a; drop database;`,如果后端程序执行了这条语句会怎么样呢?后果很严重,因为会`drop database`,届时真的是欲哭无泪了。类似的情况还很多,比如还可以输入`<input type="text" />`,结果出现了一个输入框,如果是`<form action="..."`,会造成跨站攻击了。这方面的问题还不少呢,读者有空可以到网上搜一下所谓sql注入问题,能了解更多。
174+
175+
所以,后端也要转义。
176+
177+
转义是不是很麻烦呢?
178+
179+
Tornado为你着想了,因为存在以上转义问题,而且会有粗心的程序员忘记了,于是Tornado中,模板默认为自动转义。这是多么好的设计呀。于是所有表单输入的,你就不用担心会遇到上述问题了。
180+
181+
为了能够体会自动转义,不妨在登录框中输入上面那样字符,然后可以用print语句看一看,后台得到了什么。
182+
183+
>print语句,在python3中是print()函数,在进行程序调试的时候非常有用。经常用它把要看个究竟的东西打印出来。
184+
185+
自动转义是一个好事情,但是,有时候会不需要转义,比如想在模板中这样做:
186+
187+
<!DOCTYPE html>
188+
<head>
189+
<meta charset="UTF-8">
190+
<meta name="viewport" content="width=device-width, initial-scale=1" />
191+
<title>Learning Python</title>
192+
</head>
193+
<body>
194+
<h2>登录页面</h2>
195+
<p>用用户名为:{{user}}登录</p>
196+
<form method="POST">
197+
<p><span>UserName:</span><input type="text" id="username"/></p>
198+
<p><span>Password:</span><input type="password" id="password" /></p>
199+
<p><input type="BUTTON" value="登录" id="login" /></p>
200+
</form>
201+
{% set website = "<a href='http://www.itdiffer.com'>welcome to my website</a>" %}
202+
{{ website }}
203+
<script src="{{static_url("js/jquery.min.js")}}"></script>
204+
<script src="{{static_url("js/script.js")}}"></script>
205+
</body>
206+
207+
这是index.html的代码,我增加了`{% set website = "<a href='http://www.itdiffer.com'>welcome to my website</a>" %}`,作用是设置一个变量,名字是website,它对应的内容是一个做了超链接的文字。然后在下面使用这个变量`{{ website }}`,本希望能够出现的是有一行字“welcome to my website”,点击这行字,就可以打开对应链接的网站。可是,看到了这个:
208+
209+
![](./3images/30603.png)
210+
211+
下面那一行,把整个源码都显示出来了。这就是因为自动转义的结果。这里需要的是不转义。于是可以将`{{ website }}`修改为:
212+
213+
{% raw website %}
214+
215+
表示这一行不转义。但是别的地方还是转义的。这是一种最推荐的方法。
216+
217+
![](./3images/30604.png)
218+
219+
如果你要全转义,可以使用:
220+
221+
{% autoescape None %}
222+
{{ website }}
223+
224+
貌似省事,但是我不推荐。
225+
226+
##几个备查函数
227+
228+
下面几个函数,放在这里备查,或许在某些时候用到。都是可以使用在模板中的。
229+
230+
- escape(s):替换字符串s中的&、<、>为他们对应的HTML字符。
231+
- url_escape(s):使用urllib.quote_plus替换字符串s中的字符为URL编码形式。
232+
- json_encode(val):将val编码成JSON格式。
233+
- squeeze(s):过滤字符串s,把连续的多个空白字符替换成一个空格。
234+
235+
此外,在模板中也可以使用自己编写的函数。但不常用。所以本教程就不啰嗦这个了。
236+
237+
------
238+
239+
[总目录](./index.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[上节:用tornado做网站(3)](./305.md)&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[下节:用tornado做网站(5)](./307.md)
240+
241+
如果你认为有必要打赏我,请通过支付宝:**qiwsir@126.com**,不胜感激。

3code/web/handlers/index.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@
66

77
class IndexHandler(tornado.web.RequestHandler):
88
def get(self):
9-
self.render("index.html")
9+
usernames = mrd.select_columns(table="users",column="username")
10+
one_user = usernames[0][0]
11+
self.render("index.html", user=one_user)
1012

1113
def post(self):
1214
username = self.get_argument("username")
1315
password = self.get_argument("password")
14-
#self.write(username)
1516
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
1617
if user_infos:
1718
db_pwd = user_infos[0][2]
1819
if db_pwd == password:
19-
self.write("welcome you: " + username)
20+
self.write(username)
2021
else:
2122
self.write("your password was not right.")
2223
else:

3code/web/handlers/index.pyc

758 Bytes
Binary file not shown.

3code/web/handlers/user.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env python
2+
# coding=utf-8
3+
4+
import tornado.web
5+
import methods.readdb as mrd
6+
7+
class UserHandler(tornado.web.RequestHandler):
8+
def get(self):
9+
username = self.get_argument("user")
10+
user_infos = mrd.select_table(table="users",column="*",condition="username",value=username)
11+
self.render("user.html", users = user_infos)

3code/web/handlers/user.pyc

922 Bytes
Binary file not shown.

3code/web/methods/__init__.pyc

109 Bytes
Binary file not shown.

3code/web/methods/db.pyc

347 Bytes
Binary file not shown.

3code/web/methods/readdb.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ def select_table(table, column, condition, value ):
1010
return lines
1111

1212

13+
def select_columns(table, column ):
14+
sql = "select " + column + " from " + table
15+
cur.execute(sql)
16+
lines = cur.fetchall()
17+
return lines
18+

3code/web/methods/readdb.pyc

704 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)