官术网_书友最值得收藏!

1.3 表單和模板操作

在Tornado框架中可以靈活地使用表單和模板技術,通過使用這些技術可以實現動態Web功能。

1.3.1 一個基本的注冊表單

在下面的實例文件001.py中,首先實現一個讓用戶填寫注冊信息的HTML表單,然后顯示表單處理結果。

源碼路徑:daima\1\1-3\001.py

import os.path
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
from tornado.options import define, options
define("port", default=8001, help="運行在指定端口", type=int)
class IndexHandler(tornado.web.RequestHandler):
      def get(self):
           self.render('index.html')
class PoemPageHandler(tornado.web.RequestHandler):
      def post(self):
           noun1 = self.get_argument('noun1')
           noun2 = self.get_argument('noun2')
           verb = self.get_argument('verb')
           noun3 = self.get_argument('noun3')
           self.render('poem.html', roads=noun1, wood=noun2, made=verb,
                      difference=noun3)
if __name__ == '__main__':
     tornado.options.parse_command_line()
     app = tornado.web.Application(
          handlers=[(r'/', IndexHandler), (r'/poem', PoemPageHandler)],
          template_path=os.path.join(os.path.dirname(__file__), "templates")
     )
     http_server = tornado.httpserver.HTTPServer(app)
     http_server.listen(options.port)
     tornado.ioloop.IOLoop.instance().start()

為了突出Web程序界面的美觀性,接下來我們將使用模板技術。框架Tornado自身提供了一個輕量級的模板模塊tornado.template,用于快速并且靈活地實現模板功能。我們將模板文件保存在“templates”文件夾中,其中文件index.html作為注冊表單。具體實現代碼如下。

源碼路徑:daima\1\1-3\templates\index.html

<!DOCTYPE html>
<html>
     <head><title>會員登錄</title></head>
     <body>
          <h1>輸入注冊信息.</h1>
          <form method="post" action="/poem">
          <p>用戶名<br><input type="text" name="noun1"></p>
          <p>密碼<br><input type="text" name="noun2"></p>
          <p>確認密碼<br><input type="text" name="verb"></p>
          <p>性別<br><input type="text" name="noun3"></p>
          <input type="submit">
          </form>
     </body>
</html>

模板文件poem.html用于顯示注冊結果信息。具體實現代碼如下。

源碼路徑:daima\1\1-3\templates\poem.html

<!DOCTYPE html>
<html>
     <head><title>注冊結果</title></head>
     <body>
          <h1>下面是你的注冊信息</h1>
          <p>用戶名:{{roads}}<br>密碼:{{wood}}<br>確認密碼:{{made}}<br>性別:
             {{difference}}.</p>
     </body>
</html>

開始調試本實例。首先運行前面的Python文件001.py,然后在瀏覽器中輸入http://localhost:8001/,接下來會顯示注冊表單,這是由模板文件index.html實現的。執行效果如圖1-10所示。在表單中輸入注冊信息,并單擊“提交查詢內容”按鈕后顯示注冊結果,這是由模板文件poem.html實現的。執行效果如圖1-11所示。

圖1-10 注冊表單

圖1-11 注冊結果

在上面的實例文件001.py中,定義了RequestHandler子類,并把它們傳給tornado.web.Application對象。通過如下代碼向Application對象中的init()方法傳遞一個template_path參數。

template_path=os.path.join(os.path.dirname(__file__), "templates")

參數template_path的功能是告訴Tornado模板文件的具體位置,模板是一個允許你嵌入Python代碼片段的HTML文件。通過上述代碼告訴Python,在Tornado應用文件相同目錄下的templates文件夾中尋找模板文件。當告訴Tornado在哪里可以找到模板文件后,就可以使用類RequestHandler中的render()函數告訴Tornado讀入模板文件,插入其中的模板代碼,并返回結果給瀏覽器。例如,在IndexHandler中通過如下代碼告訴Tornado在文件夾“templates”下找到一個名為index.html的文件,讀取其中的內容,并發送給瀏覽器。

self.render('index.html')

1.3.2 在模板中使用函數

在框架Tornado中,為模板功能提供了如下內置函數。

?escape(s):替換字符串s中的&、<、>為它們對應的HTML字符。

?url_escape(s):使用urllib.quote_plus替換字符串s中的字符為URL編碼形式。

?json_encode(val):將val編碼成JSON格式。(在系統底層,這是一個對JSON庫的dumps函數的調用。)

?squeeze(s):過濾字符串s,把連續的多個空白字符替換成一個空格。

在模板中可以使用一個自己編寫的函數,這時只需要將函數名作為模板的參數傳遞即可,就像使用其他變量一樣。例如:

>>> from tornado.template import Template
>>> def disemvowel(s):
...    return ''.join([x for x in s if x not in 'aeiou'])
...
>>> disemvowel("george")
'grg'
>>> print Template("my name is {{d('mortimer')}}").generate(d=disemvowel)
my name is mrtmr

再看下面的演示實例。首先在模板文件界面中提示用戶輸入兩個文本:“源”文本和“替代”文本。單擊“提交”按鈕后會返回替代文本的一個副本,并將其中每個單詞替換成源文本中首字母相同的某個單詞。本實例包括4個文件:002.py(Tornado程序)、style.css(CSS文件)、index.html和munged.html(Tornado模板)。

(1)在Python文件002.py中定義了兩個請求處理類——IndexHandler和MungedPageHandler。具體實現代碼如下。

源碼路徑:daima\1\1-3\moban2\002.py

from tornado.options import define, options
define("port", default=8001, help="運行給定的端口", type=int)
class IndexHandler(tornado.web.RequestHandler):
      def get(self):
           self.render('index.html')
class MungedPageHandler(tornado.web.RequestHandler):
      def map_by_first_letter(self, text):
           mapped = dict()
           for line in text.split('\r\n'):
                  for word in [x for x in line.split(' ') if len(x) > 0]:
                       if word[0] not in mapped: mapped[word[0]] = []
                       mapped[word[0]].append(word)
           return mapped
     def post(self):
           source_text = self.get_argument('source')
           text_to_change = self.get_argument('change')
           source_map = self.map_by_first_letter(source_text)
           change_lines = text_to_change.split('\r\n')
           self.render('munged.html', source_map=source_map, change_lines=change_lines,
                       choice=random.choice)
if __name__ == '__main__':
      tornado.options.parse_command_line()
      app = tornado.web.Application(
           handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],
           template_path=os.path.join(os.path.dirname(__file__), "templates"),
           static_path=os.path.join(os.path.dirname(__file__), "static"),
           debug=True
      )
      http_server = tornado.httpserver.HTTPServer(app)
      http_server.listen(options.port)
      tornado.ioloop.IOLoop.instance().start()

?類IndexHandler——簡單渲染了index.html中的模板,其中包括一個允許用戶發送一個源文本(在source域中)和一個替換文本(在change域中)到/poem的表單。

?類MungedPageHandler——用于處理到/poem的POST請求。當一個請求到達時,它對傳入的數據進行一些基本的處理,然后為瀏覽器渲染模板。map_by_first_letter方法將傳入的文本(從source域)分割成單詞,然后創建一個字典,其中每個字母表中的字母對應文本中所有以指定字母開頭的單詞(我們將其放入一個叫作source_map的變量中)。再把這個字典和用戶在替代文本(表單的change域)中指定的內容一起傳給模板文件munged.html。此外,我們還將Python標準庫的random.choice函數傳入模板,這個函數以一個列表作為輸入,返回列表中的任一元素。

?參數static_path——指定了應用程序放置靜態資源的目錄,如圖像、CSS文件、JavaScript文件等。

?在編寫Web應用程序時,總希望提供像樣式表、JavaScript文件和圖像這樣不需要為每個文件編寫獨立處理程序的“靜態內容”。例如,可以通過向Application類的構造函數傳遞一個名為static_path的參數來告訴Tornado從文件系統的一個特定位置提供靜態文件。下面是Alpha Munger中的相關代碼片段。

app = tornado.web.Application(
     handlers=[(r'/', IndexHandler), (r'/poem', MungedPageHandler)],
     template_path=os.path.join(os.path.dirname(__file__), "templates"),
     static_path=os.path.join(os.path.dirname(__file__), "static"),
     debug=True
)

這樣設置了一個當前應用目錄下名為static的子目錄作為static_path的參數。現在應用將以讀取static目錄下的filename.ext來響應諸如/static/filename.ext的請求,并在響應的主體中返回。

(2)模板文件index.html用于顯示主表單界面。具體實現代碼如下。

源碼路徑:daima\1\1-3\moban2\templates\index.html

<!DOCTYPE html>
<html>
     <head>
          <link rel="stylesheet" href="{{ static_url("style.css") }}">
          <title>操作</title>
     </head>
     <body>
          <h1>替換操作</h1>
          <p>在下面輸入兩個文本,替換文本將用源文本中同一字母開頭的單詞替換單詞。</p>
          <form method="post" action="/poem">
          <p>Source text<br>
               <textarea rows=4 cols=55 name="source"></textarea></p>
          <p>Text for replacement<br>
               <textarea rows=4 cols=55 name="change"></textarea></p>
          <input type="submit">
          </form>
     </body>
</html>

在Tornado模板中提供了static_url()函數來生成static目錄下文件的URL。下面是在文件index.html中static_url調用的代碼。

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

通過上述代碼調用static_url生成了URL的值,并輸出類似下面的代碼。

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

此處為什么使用static_url而不是在模板中使用硬編碼方式呢?有如下兩個原因。

?函數static_url()創建了一個基于文件內容的散列值,并將其添加到URL末尾(查詢字符串的參數v)。這個散列值確保瀏覽器總是加載一個文件的最新版而不是之前的緩存版本。無論是在應用的開發階段,還是在部署到生產環境使用時,這個方式都非常有用,因為用戶不必再為了看到靜態內容而清除瀏覽器緩存了。

?可以改變應用URL的結構,而不需要改變模板中的代碼。例如,可以配置Tornado響應來自路徑/s/filename.ext的請求時提供靜態內容,而不是默認的/static路徑。如果使用static_url而不是硬編碼,代碼不需要改變。比如,要把靜態資源從我們剛才使用的/static目錄移到新的/s目錄,可以簡單地改變靜態路徑static為s,然后每個使用static_url包裹的引用都會自動更新。如果你在每個引用靜態資源的文件中硬編碼靜態路徑部分,將不得不手動修改每個模板。

(3)模板文件munged.html用于顯示替換結果。具體實現代碼如下。

源碼路徑:daima\1\1-3\moban2\templates\munged.html

<!DOCTYPE html>
<html>
     <head>
          <link rel="stylesheet" href="{{ static_url("style.css") }}">
          <title>結果</title>
     </head>
     <body>
          <h1>現在的文本是</h1>
          <p>
{% for line in change_lines %}
     {% for word in line.split(' ') %}
          {% if len(word) > 0 and word[0] in source_map %}
               <span class="replaced"
                          title="{{word}}">{{ choice(source_map[word[0]]) }}</span>
          {% else %}
               <span class="unchanged" title="unchanged">{{word}}</span>
          {% end %}
     {% end %}
               <br>
{% end %}
          </p>
     </body>
</html>

在上述代碼中迭代替代文本中的每行,再迭代每行中的每個單詞。如果當前單詞的第一個字母是source_map字典的一個鍵,則使用random.choice函數從字典的值中隨機選擇一個單詞并展示它。如果字典的鍵中沒有這個字母,則展示源文本中的原始單詞。每個單詞包括一個span標簽,其中的class屬性指定這個單詞是替換后的(class="replaced")還是原始的(class="unchanged")。(我們還將原始單詞放到了span標簽的title屬性中,以便于用戶在鼠標指針經過單詞時可以查看哪些單詞被替換。下面看執行效果。假設在表單中分別輸入“from tornado.template import Template”和“print Template("my name is {{d('mortimer')}}").generate(d=disemvowel)”,如圖1-12所示。

圖1-12 輸入表單

單擊“提交查詢內容”按鈕后執行替換操作,并顯示替換后的結果,如圖1-13所示。

圖1-13 替換后的結果

主站蜘蛛池模板: 松潘县| 米脂县| 云梦县| 曲沃县| 奉化市| 丰县| 招远市| 宁津县| 荔波县| 酉阳| 马公市| 隆化县| 成安县| 财经| 大荔县| 团风县| 靖西县| 哈巴河县| 宁阳县| 仙桃市| 农安县| 乌兰察布市| 义乌市| 白银市| 荣昌县| 象山县| 金川县| 温州市| 澄迈县| 稻城县| 金寨县| 洛扎县| 青田县| 内江市| 忻州市| 天全县| 于都县| 昌都县| 泌阳县| 玛纳斯县| 呼伦贝尔市|