Using Django Inside the Tornado Web Server
Inspired by Eric Florenzano’s talk, Using Django in Non-Standard Ways (slides in PDF) at DjangoCon and the announcement of Tornado (tornadoweb.org), I decided to try building a small application using the Django Form library and Django ORM inside Tornado. The process proved easier than I expected, especially with Russell Keith-Magee being able to provide guidance on demand.
Step 1: Create Your Database
While Russell explained that it should be possible to get commands like syncdb running outside of a traditional Django project, it was outside the scope of this small experiment. Instead, I created a Sqlite database manually. For those of you that have forgotten how to do this, this will get you started:
# sqlite3 dev.db sqlite> CREATE TABLE message (id integer primary key, subject varchar(30), content varchar(250)); sqlite> insert into message values(1, 'subject', 'cool stuff'); sqlite> SELECT * from message;
Step 2: Write Your Application
With the database in place, you can create your application in a single file. We’ll call ours dj_tornado.py.
import sys import os import tornado.httpserver import tornado.ioloop import tornado.web # django settings must be called before importing models from django.conf import settings settings.configure(DATABASE_ENGINE='sqlite3', DATABASE_NAME='dev.db') from django import forms from django.db import models # Define your Model class Message(models.Model): """ A Django model class. In order to use it you will need to create the database manually. sqlite> CREATE TABLE message (id integer primary key, subject varchar(30), content varchar(250)); sqlite> insert into message values(1, 'subject', 'cool stuff'); sqlite> SELECT * from message; """ subject = models.CharField(max_length=30) content = models.TextField(max_length=250) class Meta: app_label = 'dj' db_table = 'message' def __unicode__(self): return self.subject + "--" + self.content # Define your Form class DjForm(forms.Form): subject = forms.CharField(max_length=100, required=True) content = forms.CharField() # Define the class that will respond to the URL class ListMessagesHandler(tornado.web.RequestHandler): def get(self): messages = Message.objects.all() self.render("templates/index.html", title="My title", messages=messages) class FormHandler(tornado.web.RequestHandler): def get(self): form = DjForm() self.render("templates/form.html", title="My title", form=form) def post(self): data = { 'subject':self.request.arguments['subject'][0], 'content':self.request.arguments['content'][0], } form = DjForm(data=data) if form.is_valid(): message = Message(**form.cleaned_data) message.save() self.redirect('/') else: self.render("templates/form.html", title="My title", form=form) # map the Urls to the class application = tornado.web.Application([ (r"/", ListMessagesHandler), (r"/form/", FormHandler), ]) # Start the server if __name__ == "__main__": http_server = tornado.httpserver.HTTPServer(application) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start()
Step 3: Create Some Templates
As you have seen in the code above I am using 2 simple templates to render the responses. Tornado syntax is very similar to Django/Jinja templates so Django developers should feel right at home here.
<!-- index.html --> <html> <head> <title>{{ title }}</title> </head> <body> <ul> {% for message in messages %} <li>{{ escape(message.subject) }} -- {{ escape(message.content) }}</li> {% end %} </ul> <a href="/form/">Add</a> </body> </html>
<!-- form.html --> <html> <body> <form action="/form/" method="post"> {{ form }} <input type="submit" value="Submit" /> </form> </body> </html>
Step 4: Start Your Server
This is easy: # python dj_tornado.py
Step 5: Profit!
Point your browser to http://127.0.0.1:8888 and enjoy your toy Tornado application.
While this application itself doesn’t do much of anything exciting, it should give you a blueprint for how to use Django in “non-standard” ways and show you how to bring some of your familiar Django tools into different applications.
You can find the code example at http://bitbucket.org/yml/dj_tornado/. I would be glad hear your opinion about this approach.
Comments
Got something to say?
Our Products
Categories
- accessiblity
- code
- company news
- django
- gondola
- open source
- portfolio
- presentation
- pro tip
- review
- screencast
- seo
- software
- subversion
- trailmapping
- wordpress
Archives
- February, 2010
- December, 2009
- November, 2009
- October, 2009
- September, 2009
- June, 2009
- April, 2009
- February, 2009
Elsewhere
What we’ve been up to online
-
@gregnewman Yeah, we actually use Wave quite a bit. Found it to be great for longer-running asynchronous group conversations.
Pete, 0 minutes ago -
Is it just me or are all contacts/avatars missing from your Google Wave too? #wavefail
Pete, 0 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 17 hours, 52 minutes ago -
pushed to inline-widget at lincolnloop/django-geotagging
Pete, 17 hours, 52 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 19 hours, 45 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 19 hours, 47 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 19 hours, 49 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 19 hours, 56 minutes ago -
pushed to master at lincolnloop/django-geotagging
Pete, 20 hours, 35 minutes ago -
Tips for remote workers: +1 for using your VCS/Issue Tracker to keep everyone in the loop efficiently http://bit.ly/bYjJNv
Pete, 1 day ago -
Lots of love for the Django community in the latest @nasuni blog post http://bit.ly/dkrVk5
Pete, 1 day ago -
4 simple tips for wannabe remote workers « Fetchez le Python
Tips for remote workers: +1 for using your VCS/Issue Tracker to keep everyone in the loop efficiently
Pete, 1 day ago -
Django on AppEngine has come a long way. I'm watching a local management command populate my GAE datastore. Great work @wkornewald
Pete, 6 days, 19 hours ago -
Oh noes! Our venerable @cmheisel is moving on to greener pastures. Help us find a new Project Manager! http://bit.ly/9tjgPx
Pete, 1 week ago -
pushed to master at lincolnloop/django-startproject
Pete, 1 week ago


In my view using Django (or really any other normal WSGI framework) inside of Tornado is rather pointless. The advantage of using Tornado is that it does non blocking IO, however a Django request is going to block for it’s entirety, so you really don’t get anything by using this.
Yeah, the advantage of this in a production environment is debatable. I think it was an experiment in re-using Django bits outside of a traditional Django project more than anything else.
The ability to get any one of the Python micro-frameworks up and running quickly, only using the Django bits you need, is compelling. The techniques Yann uses here could be applied not only to Lightning, but anyplace you wanted to use Django modules.
But he’s not using Django’s request/response objects or it’s middleware handling or anything else related to the HTTP request. He’s only using the models and forms objects which, I think, are wholly unrelated to the asynchronous features that Tornado gives you.
I agree completely, but in my experience the longest portions of any web request are SQL queries, and those do block :)
Bret Taylor, said this about synchronous DB queries:
We experimented with different async DB approaches, but settled on
synchronous at FriendFeed because generally if our DB queries were
backlogging our requests, our backends couldn’t scale to the load
anyway. Things that were slow enough were abstracted to separate
backend services which we fetched asynchronously via the async HTTP
module.
http://groups.google.com/group/python-tornado/browse_thread/thread/9a08f5f08cdab108
Thanks for posting this, by the way, Yann!
I just wish that Django could be async like tornado someday….
In a post in my own blog http://trespams.com/2009/09/14/django-cherrypy-vs-tornado/ I compared Tornado WSGI with CherryPy.
Actually we’re using ngnix as reverser proxy and to serve media, that connects with one o more CherryPy servers that runs Django via WSGI.
With this configuration Tornado could server about 16% more connections that CherryPy.
You could ask me why this configuration, of course. We have found that this gives a lot of freedoom in the way we can configure our servers and scale the applications. With CherryPy the req/s was lower that with Apache+mod_python but it was good enougth and the CPU and Memory required was about 10%.
Acually we’re testing Tornado as a replacement of CherrPy. Pehaps we could use it as a microframeworks to act as a REST server or XML server, but for normal web applications our plans are to continue with Django.