Задание url для обработчиков Tornado

В tornado, для привязки обработчиков к url’ам, можно передать список из кортежей (url regex, handler) при инициализации приложения:

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/some/path/page/(?P<pk>[0-9]+)$", PageHandler),
])

Но не секрет, что гораздо удобнее использовать обертку tornado.web.url, которая позволяет присваивать имена для путей (похожа на django’вский url).

Однако, в паре рабочих проектах, с которыми приходилось работать, эта обертка не использовалась. А так же в некоторых примерах из tornado документации (раз, два, три) тоже используются обычные кортежи, без url, что может сбить с толку. Поэтому считаю полезным описать те преимущества, которые дает эта обертка.

Итак, какие неудобства мы испытываем при работе без использования url.

Без url()

Чтобы в коде или в шаблоне воспроизвести нужный путь, приходится вручную вводить строку.

Пример

app.py:

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("home.html", title="My title", pages=[1, 2, 3])

class PageHandler(tornado.web.RequestHandler):
    def get(self, page_n):
        email_text = "Please visit this page: '/some/path/page/{page_n}/'".format(
            page_n=1)
        send_email('some@person.com', email_text)
        self.render("page.html", title="Page", page_n=page_n)

application = tornado.web.Application([
    (r"/", MainHandler),
    (r"/some/path/page/(?P<page_n>[0-9]+)/$", PageHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

home.htm:

<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <div>View pages:</div>
     <ul>
       {% for page_n in pages %}
         <li><a href="/some/path/page/{{ page_n }}/">{{ page_n }}</a></li>
       {% end %}
     </ul>
   </body>
 </html>

page.html:

<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <div>You are viewing page #{{ page_n }}</div>
     <div>Back to <a href="/">Home<a></div>
   </body>
 </html>

Видим, что даже в этом простом коде пришлось трижды повторять путь /some/path/page/. А если нам понадобится чуть-чуть изменить эту строку? Придется делать автозамену, что неудобно и чревато ошибками. К тому же, некоторые пути могут быть громоздкими, что ухудшает читабельность кода.

Используя url()

Этот же пример, но с оберткой url:

app.py:

import tornado.ioloop
import tornado.web
from tornado.web import url


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("home.html", title="My title", pages=[1, 2, 3])


class PageHandler(tornado.web.RequestHandler):
    def get(self, page_n):
        email_text = "Please visit this page: '{url}'".format(
            url=self.reverse_url('page', 1))
        send_email('some@person.com', email_text)
        self.render("page.html", title="Page", page_n=page_n)


application = tornado.web.Application([
    url(r"/", MainHandler, name="home"),
    url(r"/some/path/page/(?P<page_n>[0-9]+)/$", PageHandler, name="page"),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

home.html:

<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <div>View pages:</div>
     <ul>
       {% for page_n in pages %}
         <li><a href="{{reverse_url('page', page_n)}}">{{ page_n }}</a></li>
       {% end %}
     </ul>
   </body>
 </html>

page.html:

<html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
     <div>You are viewing page #{{ page_n }}</div>
     <div>Back to <a href="{{reverse_url('home')}}">Home<a></div>
   </body>
 </html>

Путям присвоены значащие имена, которые используются для воспроизведения url’a с помощью метода reverse_url. Если нам нужно будет подправить какой-либо путь, то мы сделаем это лишь в одном месте. Гораздо удобнее!