Tornadoを使ってみる

FormとTemplate


WEBフレームワークの最も重要な機能はテンプレートに好きな文字を埋め込むレンダリングの機能です。
こ ちらにTornadoのサンプルが公開されています。

こ ちらのmain.pyを実行すると以下の表示となります。


テンプレートはtemplate_basics/bookstore/templatesに格納されています。
index.htmlのテンプレートを使いますが、最初にmain.htmlを取り込んでいます。
main.htmlをひな形として<block header><block body><block footer>をindex.htmlの内容で差し替えています。

tornadoのtemplate enjinにはpythonコードを実行する機能が有ります。
詳しくはこちらに 公開されていますが、スクリプト内でローカル変数が使えたり、if〜elif〜else〜endとやりたい放題です。



こ ちらのmain.pyでは、page_title header_text footer_textの3つの変数をindex.htmlに渡しています。

複数の変数は、JSON形式の変数としてテンプレートに渡すことができます。
JSON形式の変数として渡す場合、pythonコードは以下の様になります。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        templateData = {
        'text1' : 'aaa',
        'text2' : 'bbb',
        'text3' : 'ccc'
        }
        self.render("index1.html", **templateData)

テンプレートでは以下の様に変数を参照します。
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    Hello World!!<br>
    {{ text1 }}<br>
    {{ text2 }}<br>
    {{ text3 }}<br>
  </body>
</html>

以下のコードでも同じ結果となります。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index1.html", text1="aaa", text2="bbb", text3="ccc")



複数の変数は、list変数、あるいはtuple変数としてテンプレートに渡すこともできます。
list変数として渡す場合、pythonコードは以下の様になります。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        # Build list
        texts = []
        texts.append("AAA")
        texts.append("BBB")
        texts.append("CCC")
        self.render("index2.html", text=texts)

テンプレート側では変数名にインデックスを付けて参照します。
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    Hello World!!<br>
    {{ text[0] }}<br>
    {{ text[1] }}<br>
    {{ text[2] }}<br>
  </body>
</html>



listの入れ子、あるいはlist構造のtupleをテンプレートに渡すと簡単にテーブルを作ることができます。
listの入れ子を渡す場合、pythonコードは以下の様になります。
class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        # Build list
        texts = []
        texts.append("OrangePi PC")
        texts.append("1024MB")
        texts.append("H3")
        lists = []
        lists.append(texts)

        texts = []
        texts.append("OrangePi Lite")
        texts.append("512MB")
        texts.append("H3")
        lists.append(texts)

        texts = []
        texts.append("OrangePi Zero")
        texts.append("512MB")
        texts.append("H2+")
        lists.append(texts)

        self.render("index3.html", lists=lists)

テンプレート側ではpythonコードを使って2重ループを組みます。
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <table cellspacing="2" cellpadding="2" border="1">
      <tbody>
        <tr>
          <th>Model</th>
          <th>Memory</th>
          <th>SOC</th>
        </tr>
        {% for list in lists %}
          <tr>
            {% for item in list %}
              <td>{{item}}</td>
            {% end %}
          </tr>
        {% end %}
      </tbody>
    </table>
  </body>
</html>

結果はこのようになります。


テンプレート側に行番号を判定するコードを埋め込んでみました。
<html>
  <head>
    <title>Hello World</title>
  </head>
  <body>
    <table cellspacing="2" cellpadding="2" border="1">
  <body>
    <table cellspacing="2" cellpadding="2" border="1">
      <tbody>
        <tr>
          <th>Model</th>
          <th>Memory</th>
          <th>SOC</th>
        </tr>
        {% set row = 0 %}
        {% for list in lists %}
          {%if (row % 2) == 0 %}
            <tr bgcolor="#ffffc0">
          {% else %}
            <tr bgcolor="#c0ffc0">
          {% end %}
            {% for item in list %}
              <td>{{item}}</td>
            {% end %}
          </tr>
          {% set row = row+1 %}
        {% end %}
      </tbody>
    </table>
  </body>
</html>

1行おきに背景色が変わります。




main.htmlをひな形としてブロックを差し替える機能はTornado特有の機能です。
そこで、最も簡単なサンプルを紹介します。
最初にひな形となるtemplates/main.htmlを作ります。
<html>
<body>
 <header>
 {% block header %}{% end %}
 </header>
 <content>
 {% block body %}{% end %}
 </content>
 <footer>
 {% block footer %}{% end %}
 </footer>
</body>
</html>

見てわかる通り全ての要素がブロックで構成されています。
次にこれを拡張するtemplates/index.htmlを作ります。
{% extends main.html %}
{% block header %}
 <h1>Hello world!</h1>
{% end %}

以下のスクリプトでindex.htmlを表示します。
#
# Basics of Blocks
#
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import os.path

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[(r'/', IndexHandler)],
        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.current().start()

main.htmlの<block header>をindex.htmlの内容で差し替えて表示します。
この機能を使えば全てのページを統一した見た目にすることができます。
main.htmlを変更すると、main.htmlを参照している全てのページが一斉に変わります。




こちら
のpoemmaker.pyを実行すると以下の表示となります。


送信ボタンを押すと以下の表示となります。




以下のコードでtextに入力した文字を取り出すことができます。
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('<html><body><form action="/show" method="POST">'
                   '<input type="text" name="message">'
                   '<input type="submit" value="Submit">'
                   '</form></body></html>')

class ShowHandler(tornado.web.RequestHandler):
    def post(self):
        self.set_header("Content-Type", "text/plain")
        self.write("You wrote " + self.get_body_argument("message"))

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/", IndexHandler),
            (r"/show", ShowHandler),
        ],debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

このコードを実行すると以下のフォームが表示されます。


submitボタンを押すとShowHandlerクラス内のpost()でデータを受け取ります。


以下の様に同じクラスの中にget()とpost()を作ることができます。
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('<html><body><form action="/" method="POST">'
                   '<input type="text" name="message">'
                   '<input type="submit" value="Submit">'
                   '</form></body></html>')

    def post(self):
        self.set_header("Content-Type", "text/plain")
        self.write("You wrote " + self.get_body_argument("message"))

if __name__ == "__main__":
    tornado.options.parse_command_line()
    app = tornado.web.Application(
        handlers=[
            (r"/", IndexHandler),
        ],debug=True
    )
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)
    tornado.ioloop.IOLoop.current().start()

submitボタンを押すと同じクラス内のpost()でデータを受け取ります。




以下のコードでどのボタンが押されたかを判定することができます。
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
        def get(self):
                self.write('<html><body><form action="/" method="POST">'
                   '<p><input type="submit" class="button" name="basic" value="Basic Query"></p>'
                   '<p><input type="submit" class="button" name="advanced" value="Advanced Query"></p>'
                   '</form></body></html>')

        def post(self):
                if self.get_argument('basic', None) is not None:
                        self.write('Basic Query')

                elif self.get_argument('advanced', None) is not None:
                        self.write('Advanced Query')

if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(
                handlers=[
                        (r"/", IndexHandler),
                ],debug=True
        )
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.current().start()

このコードを実行すると以下のフォームが表示されます。


submitボタンを押すとpost()でデータを受け取り、どのボタンが押されたかを判定します。





フォーム内に複数のチェックボックスが有る場合、以下のコードでどれが選ばれているかを判定することができます。
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web

from tornado.options import define, options
define("port", default=8000, help="run on the given port", type=int)

class IndexHandler(tornado.web.RequestHandler):
        def get(self):
                self.write('<html><body><form action="/" method="POST">'
                        '<input type="checkbox" name="c1">1'
                        '<input type="checkbox" name="c2">2'
                        '<input type="checkbox" name="c3">3'
                        '<br><input type="submit" value="Submit">'
                        '</form></body></html>')

        def post(self):
                if self.get_argument('c1', None) is not None:
                        self.write('1<br>')

                if self.get_argument('c2', None) is not None:
                        self.write('2<br>')

                if self.get_argument('c3', None) is not None:
                        self.write('3<br>')

if __name__ == "__main__":
        tornado.options.parse_command_line()
        app = tornado.web.Application(
                handlers=[
                        (r"/", IndexHandler),
                ],debug=True
        )
        http_server = tornado.httpserver.HTTPServer(app)
        http_server.listen(options.port)
        tornado.ioloop.IOLoop.current().start()

このコードを実行すると以下のフォームが表示されます。


submitボタンを押すとpost()でデータを受け取り、どのチェックボックスが選択されているかを判定します。


チェックボックスをコード側で生成する場会、名前と値をリストに入れてテンプレート側に渡します。
以下の例ではリストの0番目に名前、1番目に値を入れて、テンプレートに渡しています。
これでcheckboxに一意の名前が付きます。
<tbody>
  {% for check in checkList %}
    <div>
    <input type="checkbox" name="{{ check[0] }}" value="{{ check[1] }}">{{ check[1] }}
    </div>
  {% end %}
</tbody>

続く...