Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Крэш-курс по Tornado для Django-программистов

Крэш-курс по Tornado для Django-программистов

Зачем нужен Tornado? Ок, Где мой manage.py? А настройки? Кажется, здесь нет ORM. И странные шаблоны... И нет админки! И это ещё цветочки.

Avatar for Moscow Python Meetup

Moscow Python Meetup

February 10, 2015
Tweet

More Decks by Moscow Python Meetup

Other Decks in Technology

Transcript

  1. About me • Alexey Kinev, 32 y.o.
 programmer at 05Bit

    & Hypple • Working with Django since 2008,
 with Tornado since 2012 • GitHub:
 https://github.com/rudyryk
 https://github.com/05bit

  2. Notes on code • Code samples are for Python 3.x

    • For “yield from” Python 3.3+ is required • Full code samples here:
 https://github.com/rudyryk/python-samples
  3. Request / response, templates, security, localization, testing 3rd party libs

    “Batteries included”: - ORM + migrations - sessions - forms - admin - caching, middleware etc. Asynchronous
 micro-framework WSGI
 full-stack Tornado vs Django
  4. Why Tornado? + WebSockets Trade-offs: - More freedom may lead

    to less code maintainability - Harder and more time to learn - Easier to do things wrong (and lose speed :)
  5. Show me the code! # hello_world.py import tornado.ioloop import tornado.web

    class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
  6. Hello JSON # hello_json.py # ... class MainHandler(tornado.web.RequestHandler): def get(self):

    self.write({ 'status': 'OK', 'text': "Hello, world" }) # ...
  7. Just do it async # hello_async.py # ... import tornado.gen

    from tornado.httpclient import AsyncHTTPClient class MainHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): http_client = AsyncHTTPClient() url = 'http://httpbin.org/delay/3' response = yield http_client.fetch(url) self.write(response.body) # ...
  8. And what next? • Architecture • Project structure • Database,

    ORM • Command line interface • Web administration tool
  9. Architecture: application • RequestHandler objects are created per-request • Application

    instance is passed to handlers class LoginHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): # Do login stuff ... # Suggest we have ans SMS sender client in app sms = self.application.sms_sender # Notify and redirect ...
  10. Architecture: application • Configutation is made for Application # hello_feed/main.py

    # ... class Application(tornado.web.Application): def __init__(self): handlers = [ (r"/", FeedHandler), ] settings = {'debug': True} super().__init__(handlers, **settings) if __name__ == "__main__": application = Application() # ...
  11. Architecture: RequestHandler • Important one, just read docs carefully •

    HTTP methods: get(), post(), put(), delete(), head(), options() • Before: prepare()
 After: on_finish() • Read arguments: get_argument()
 Write output: write()
  12. Architecture: IOLoop • Single threaded, not thread-safe, well… loop! if

    __name__ == "__main__": # ... # This is like "while True:" with callbacks # (handlers) for READ, WRITE and ERROR tornado.ioloop.IOLoop.instance().start()
  13. “Steal” ideas from Django! • MVC is a proven pattern

    • Unified apps structure • manage.py • ORM + web admin might be great
  14. MVC: View # handlers.py import tornado.gen from .models import Feed

    # We may treat Handlers as Views class FeedHandler(tornado.web.RequestHandler): @tornado.gen.coroutine def get(self): feed = yield Feed.fetch() self.write(feed)
  15. MVC: Model # models.py import tornado.gen from tornado.httpclient import AsyncHTTPClient

    # Model layer example: just an idea, not real feed class Feed: @classmethod @tornado.gen.coroutine def fetch(cls): http_client = AsyncHTTPClient() url = 'http://httpbin.org/delay/1' response = yield http_client.fetch(url) body = response.body.decode('utf-8') return json.loads(body)
  16. Click for manage.py • Click http://click.pocoo.org/3/ • Add commands to

    rule them all:
 start, admin, test, migrate etc.
  17. YAML for configs • http://pyyaml.org/wiki/PyYAML • Is parsed to dict

    with one line: yaml.load(data) • Supports comments, readable and nice
  18. Jinja2 vs Tornado templates • Tornado does have templates •

    Pros: less dependencies, looks familiar • Cons: less popular, one more thing to learn • When in doubt, use Jinja2 http://jinja.pocoo.org/
  19. ORM: peewee • https://github.com/coleifer/peewee • Initially was inspired by Django

    ORM, now different • Lightweight and powerfull • Has basic migrations for PostgreSQL and MySQL • Has async adapter
  20. ORM: peewee + Admin • Flask-Admin has peewee support
 https://github.com/mrjoes/flask-admin/

    • Wait! Do I also need Flask? Well yes :)
 That’s how we do stuff in micro-frameworks • Pros: Flask-Admin will run in separate process
  21. Authentication • Tornado supports secure cookies • Third-party login with

    OpenID and OAuth • Storage backend for authentication should be implemented by users code: ORM, Redis, Memcached, etc.
  22. The next big thing • asyncio was introduced in Python

    3.4 as a part of stdlib • MoscowDjango talk about asyncio
 http://moscowdjango.ru/meetup/18/tulip/ • Tornado plays great with asyncio
  23. Hello asyncio (1) • asyncio was introduced in Python 3.4

    as a part of stdlib • MoscowDjango talk about asyncio
 http://moscowdjango.ru/meetup/18/tulip/ • Tornado plays great with asyncio
  24. Hello asyncio (2) # hello_asyncio.py import asyncio import tornado.ioloop import

    tornado.web class Application(tornado.web.Application): def __init__(self): # Prepare IOLoop class to run on asyncio at very start tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOMainLoop') # ... if __name__ == "__main__": application = Application() application.listen(8888) loop = asyncio.get_event_loop() loop.run_forever() • Prepare loop before instantiation • Instantiate loop after forking
  25. Hello asyncio (3) # ... if __name__ == "__main__": #

    Prepare application server application = Application() server = tornado.httpserver.HTTPServer(application) server.bind(8888, '127.0.0.1') # Fork by number of processor cores server.start(0) # Instantiate and run loop after forking loop = asyncio.get_event_loop() application.init_with_loop(loop) loop.run_forever() • asyncio + Tornado
 multiple processes
  26. Hello asyncio (4) • Run methods as coroutines # python-samples/hello_tornado/hello_asyncio.py

    # ... class MainHandler(AsyncRequestHandler): @asyncio.coroutine def get_async(self): redis = self.application.redis yield from redis.set('my-key', 'OK') val = yield from redis.get('my-key') self.write('Hello coroutine: %s' % val)