How to Beat Rails

December 31, 2006 at 10:17 pm 26 comments

(Note: I really, really like Ruby and Rails. Anything disparaging that I say about either of them should be taken with several grains of salt. If I seem to be encouraging Pythoneers to crush Rails, it’s simply because I love both, and want all frameworks to be constantly innovating.)

Everybody with the slightest interest in web development has heard of Ruby on Rails. It thrust Ruby into the spotlight, created a hype machine that stubbornly refuses to go away, and made David Heinemeier Hansson a celebrity. I adore Rails – it’s by far the best web framework for simple CRUD apps. However, as a Python devotee, it hurt me deeply to see Ruby stealing the spotlight. As such, I embarked on a quest to find out why Rails is winning; I looked at Django, Turbogears, Pylons and After months of building the same simple CRUD app, I came to the following conclusion:

Python can beat Rails. It can grind it into a pulp in every way concievable – speed, elegance, coolness, extensiblity, organization, AJAXness, beauty, and flexibility. It can send Rails crying home to David while Guido basks in the Web 2.0 spotlight. But right now, Rails is thoroughly torching all of the Python web frameworks. The following is a list of recommendations that, if applied to a Python framework, could dislodge Rails from the position of King of the Web Frameworks.

Section 1: The Model

  1. Use SQLAlchemy. In case you haven’t heard of this amazing Python library, it’s a phenomenally slick layer on top of SQL which provides all the power of SQL wrapped inside the elegance that is Python. The advanced features it offers blow SQLObject out of the water, the documentation is top-notch, the user community is active, it supports a vast number of types of databases, and it has a remarkably extensible plugin architecture.
  2. Fix SQLAlchemy’s configuration. Unfortunately, SQLAlchemy is fundamentally broken in a number of ways. Firstly, it’s difficult to set up – any web framework that uses SA should make sure that the programmer can ignore the tedium that is setting up a session correctly, what with SessionObjects, contexts and metadata. Kudos to Turbogears for making that part easy.
  3. Use SQLAlchemy declarative layers. SQLAlchemy’s table declaration code is very verbose compared to that of SQLObject or ActiveRecord; libraries like ActiveMapper or TurboEntity allow SA tables to be declared like Python objects, a la SQLObject. TurboEntity is cool in that it makes many-to-many relationships easy and allows for inheritance. Alternatively, a visual table designer could be cool (as long as the generated code wasn’t too hideous).
  4. Support database schema migrations. One of the things that makes ActiveRecord a joy to use is its database migration system; if I make a mistake, I should be able to roll back changes without nuking my entire database. There is already a project in the works to support migrations inside SQLAlchemy; making sure that migrations play nicely with the aforementioned tools for declarative mapping will probably be quite difficult – yet I am sure that the smart cookies in the Python community are up for it.

Section 2: The View

  1. Use Django templates. They are easy to learn, they don’t lock you into writing XHTML (sorry, Genshi!) , and they provide everything a templating system should have.
  2. Make Ajax integration easy. Turbogears does this brilliantly with MochiKit; Django doesn’t do this at all. However, Rails still beats them both with tools.
  3. Use Turbogears widgets. I’ll let you on to a little secret of mine – I don’t like writing HTML. I find CSS, div’s, span’s, stylesheets, and all the other HTML miscellany to be a pain in the ass. Turbogears has widgets – Python objects designed to produce specific kinds of HTML, such as a grid for displaying data; these allow me to knock together very slick interfaces very quickly. The ToscaWidgets project aims to allow the use of Turbogears widgets in any framework; however, I couldn’t get it to work.

Section 3: The Controller

  1. Use CherryPy 3. CherryPy will be immediately familiar to those who have worked with Rails. Add on the fact that its WSGI server is really fast, and we have a winner.
  2. Use the routing systems of Pylons and Django. Yes, Pylons’ routing system pretty much a direct copy of Rails’ routes.rb system. So what? Rails got routing right. However, I feel that Django’s lends itself to prettier URL’s – as such, make the Pylons one the default, with the option of using Django’s one.
  3. Use FormEncode for validation. One of my biggest pet peeves with Rails is that sometimes the validation can be very inflexible – I had a lot of trouble with trying to tell Rails just to ignore any dollar signs entered in certain text fields. FormEncode is far more flexible.

Section 4: Miscellany

  1. Use Python 2.5. Even if you’re keeping backwards compatibility, my application shouldn’t break with mounds of gibberish for an error code (I’m looking at you, Turbogears) when I forget to tell it to use Python 2.4.
  2. Use Django’s admin interface. It’s slick, functional, and easy – keep it.
  3. Have Django’s userauth system at the ready. Rails doesn’t have a built-in userauth system; if we put one in, we’ll have an immediate advantage.
  4. The Turbogears Toolbox is crucial. Having a mini-app that displays all the availiable Ajax features and widgets is invaluable – it saves hours digging through documentation and out-of-date tutorials.
  5. Have the Django people write the documentation. The documentation and tutorials for Django are the best out of any web framework; good documentation is crucial if you want people to stick with a framework.
  6. Learn from Turbogears’ extensibility. Using easy_install to add another Turbogears widget makes it show up in the Turbogears toolbox; a good Python web framework needs to be just as extensible. Rails engines don’t do the job entirely right, as they sometimes generate code that clutters up the main application – newly installed packages should be seamless.
  7. Have Unicode support. Python’s Unicode support is second only to Java (in which every string is Unicode); use this built-in advantage over Ruby in order to get a jump on Rails.
  8. Get the publicity machine going. DHH is the best software evangelist since Larry Wall; we need someone similarly charismatic and outspoken in order to push any new Python software.
  9. Make CRUD apps really easy. We need to learn from Streamlined and the Ajax scaffold generator. (Now that I think about it, extending the Django admin interface would take care of this.)

This has turned into a far longer post than I would have expected. Thanks for reading it; let me know what you think.

Entry filed under: code, django, mvc, pylons, python, rails, ruby, turbogears, webapps.

Objective-C 2.0 Blocks != Functional Programming


  • 1. vineire  |  January 18, 2007 at 1:23 am

    Very insightful. I’ve been thinking about Rails vs. Django for quite awhile. I’m just getting my feet wet with both languages, but python just feels better. I like it and seem to be picking it up much faster.

    The only bummer is that Rails seems have a much more contributions with plug-ins, etc… Any thoughts on which would be better for a newb?

  • 2. newpers  |  January 20, 2007 at 4:47 am

    Great post.

  • 3. Joe H  |  February 3, 2007 at 9:10 am

    Succinctly: Be more RESTful. I’d like to see a “web platform” that starts with being RESTful and layers on the web app conventions a la Rails and Grails as just a degenerate case.

  • 4. Niklas  |  February 3, 2007 at 11:24 am

    Great post!

  • 5. Simon G.  |  February 3, 2007 at 1:33 pm

    Interesting, and it’s good to see these frameworks compared in sensible way (i.e. not the “framework X rocks! all others suck”-type that we tend to see).

    Full disclosure, I’m a Django-fan, so here’s a few points – Django has a SQL Alchemy branch, that’s slowly progressing.

    Regarding the Schema migration, there’s been a bit of discussion, but nothing concrete AFAIK. Personally, I’m of the opinion that your model needs to be *right* before you start developing, but I know other people disagree.

    Django and AJAX – this has been debated a lot, and whilst Django has no one-click, plug-n-play AJAX framework, it’s worth noting that it does have a very easy to use serialisation framework which does allow you to fling out XML/JSON for easy AJAXing.

    Re: CherryPy, apparently running Django via CherryPy is do-able, but I’ve never done it, so don’t take my word for it!


  • 6. malcontent  |  February 3, 2007 at 10:07 pm

    Get a great testing system like rails has.

    Oh and also deployment, get something like capistrano.

  • 7. djangonaut nro. 6767  |  February 3, 2007 at 10:13 pm

    Simon. About the “slow progress” of the sqlalchemy branch, there’s no such a thing. Looking at the “svn log” output, the branch was opened at 2006-08-29 by adrian, and until now, the maintainer has been just merging the trunk changes on to the branch. There’s zero real development over there.

  • 8. jherber  |  February 3, 2007 at 11:20 pm

    your mistake is to compare a language and a set of disjoint technologies against a DSL focused on productivity by encoding a set of best practices for web development. rails is concise, cohesive, comprehensive and full of common sense. anything that beats rails will have to be written from the ground up to beat rails with productivity and practical approach as the overarching goal.

  • 9. Ian Bicking  |  February 4, 2007 at 2:47 am

    CherryPy 3 with Routes would in many ways be a funny combination — Routes would mean avoiding the CP dispatch, and it’s not clear what exactly you’d have left. Certainly the CP HTTP server is useful, but it’s completely decoupled from the rest of CP so that’s a totally independent choice. I think Pylons shows that you can implement all the other parts of CP in a much simpler manner using WSGI alone.

  • 10. Robin Munn  |  February 4, 2007 at 7:16 am

    I’m the maintainer (so far) of the Django/SQLAlchemy branch, and you’re right that there’s been basically zero progress there so far. Other commitments have kept me massively busy in the past half-year or so; I shouldn’t have agreed to take on the Django/SQLAlchemy branch when I did.

    But those other commitments are done now, and I’m free to devote a lot more time to Python programming than I was before. There are several ideas that have been kicking around in my head that I may finally be able to put into code and check in.

    I hope to have at least a minimal prototype to show at PyCon.

  • 11. mike bayer  |  February 4, 2007 at 10:15 pm

    Pylons and Paste using straight WSGI strikes me as more WSGIish (open-ended) than using CherryPy.

    and of course id vote for Mako templates ( as they are neutral of any particular framework, have all the rich features people need (inheritance, full scripting, heavy support for componentized layouts), are extremely simple and concise, and as fast as any template language written in pure Python can be.

  • 12. schlenk  |  February 5, 2007 at 8:38 pm

    Having to work with Python unicode support i must say, Pythons unicode support may be better than Ruby, but between Java and Python you should at least place Tcl, which has a far smoother unicode support layer than Python (no wonder, the people writing the Tcl unicode support sat next to the ones writing the Java unicode support and learned from them).

  • […] Thomson has written an article called How to Beat Rails that outlines some ways that Python can “beat” Rails at the web application framework […]

  • 14. Luciano Pacheco  |  February 8, 2007 at 2:46 am

    Good post !

    Another important feature IMHO is internationalization and localization in Rails it’s a pain, but TG and DJ I don’t know.

    About Templates: A important feature is: Designer frendaly and can load in browser.

    The Kid template can be loaded in the browser to preview a result, it’s very productive IMHO.


  • 15. LKRaider  |  February 11, 2007 at 6:29 pm

    What about Plone/Zope ? There is SQLAlchemy support through the Alchemist product, and easy form creation from the Plone interface with PloneFormGen product.

    Why is it missing from this review?

  • 16. Jon  |  February 12, 2007 at 4:46 am

    I’ve played with RoR a bit, and know nothing of Python, but I think that good competition can help both. If you are an advocate of either, knowing that the other side is doing cool, innovative new stuff is only going to make you work harder to show how your side is better.

    And honestly, your post about how Python can make people forget RoR makes me want to go find a nice Python tutorial and see what I’m missing.

  • 17. Adam Hoscilo  |  March 8, 2007 at 1:26 pm

    “Make Ajax integration easy. Turbogears does this brilliantly with MochiKit; Django doesn’t do this at all. However, Rails still beats them both with tools.”
    Pylons has almost all Rails WebHelpers implemented:

    “Use the routing systems of Pylons and Django.”
    the Routes are much better solution than Django’s URLs. Specially for maintaining existing urls. In Django you have to hardcode URLs in templates or controllers.

    “Get the publicity machine going.”
    I think this point is crucial, especially in Pylons case. Marketing is very important – don’t underestimate.

  • 18. Giles Bowkett  |  March 20, 2007 at 11:35 pm

    This is kind of weird. If you can see how to do it, you should do it. Nothing rallies people behind you like actually doing something.

  • 19. eduardo  |  March 21, 2007 at 6:29 pm

    I read it three times. Excellent!

  • 20. Vernon  |  March 26, 2007 at 9:54 pm

    Just a small comment to Adam Hoscilo re Django regular expression URLs: I believe the opposite of what you say is true, the URLs reside in their own file and simply point to the view functions (or controllers in normal MVC-speak), and are not in the templates or controllers. If anything the Django URLs provide the maximum in flexibility out of the available options.

  • 21. Adam Hoscilo  |  March 27, 2007 at 8:22 am

    Maybe something has changed in the topic of URL dispatching/resolving in Django that i’m not aware of.

  • 22. Jan Johannsen  |  July 18, 2007 at 1:18 am

    Time keeps moving =)

    Another Python ORM for you to play with from the guys that brought us Ubuntu, Canonical.

    Clean and lightweight API offers a short learning curve and long-term maintainability providing the following features:

    o Storm lets you efficiently access and update large datasets by allowing you to formulate complex queries spanning multiple tables using Python.
    o Storm allows you to fallback to SQL if needed (or if you just prefer), allowing you to mix “old school” code and ORM code.
    o Storm handles composed primary keys with ease (no need for surrogate keys).
    o Storm handles relationships between objects even before they were added to a database.
    o Storm works well with existing database schemas.
    o …and more.

  • 23. ken  |  August 1, 2007 at 10:39 pm

    I disagree about your ordering of Unicode implementations.

    It’s nice that in Java every string is Unicode. Or rather, Unicode 3.0. If you want to support anything outside of the BMP, you practically have to implement half of UTF-16 decoding yourself. You need 2 “char”s to represent some Unicode code points — not fun.

    Python is much nicer, because you get any Unicode character (all 21 bits) in a string. There’s also a str (ASCII) type, but you can simply skip it for now. (In Python 3, this will be fixed.)

    I’d summarize the difference as “In Java, you get Unicode 3 for free, and it’s a lot of work to support Unicode 5” versus “In Python, you get ASCII for free, and it’s little-or-no work to support Unicode 5”.

    On the top, though, are Lisp implementations like SBCL, which combine the best features of Java (every character and string is Unicode-capable) and Python (full 21-bit characters for free). This isn’t surprising: Lisp predates ASCII. 🙂

    I’ve not done any Unicode work in Tcl or Ruby yet, so I can’t speak to those.

  • 24. random8r  |  August 19, 2007 at 2:16 am

    That’s kinda funny…

    You’re like “Change Python’s frameworks so they follow Rails” because you seem to like the language and the speed and stuff like that.

    Regardless of the initial disclaimer, you’re very biased towards Python (which is fine, I’m just pointing it out).

    I personally have a strong distaste for any language which imbibes white space with meaning. It strikes me as ugly when I use it. But, hey, I’m a smalltalker at heart, and Ruby is way more smalltalkish than Python.

    This aside, the real reason I’m posting is to help you out…

    You’re missing the main point of why Rails is so successful. It’s the same reason that Apple, Dyson or even Nintendo are so successful.

    We, as REAL creators, spend far less time looking around at the competition, and spend time work on creating something that we feel and think is a beautiful thing. There can be no competition for this, because it’s inappropriate to compare.

    If you’re not following your own heart – if you’re following someone else’s lead, then you can’t help but not know where you’re going. You’re being led by others, then people are not going to become truly enpassioned about your product, however many bells and whistles there are on it.

    The reason the Python community has no “Rails” at the moment is that no one has yet created something that they truly love to use and that is a simple, elegant solution for Web Development.

    The reason all of these aforementioned communities have such enpassioned userbases is that they love the things. They’re literally created out of love, and so they have love in them. They’re therefore lovely to use. 🙂

    – Random8r

  • 25. philosophersam  |  August 28, 2007 at 12:17 am

    I love it when programming language evangelists proclaim things without any arguments. I guess my argument against Python is giving white-space a meaning in the language. My initial and current reaction that is “Blah!” I find Ruby to be more interesting and enjoyable to learn….but I would love to be shown why I’m wrong.

  • 26. kenneth gonsalves  |  December 22, 2007 at 5:35 am

    in the only known face-of between django and rails, django won handsomely. Google for snakesandrubies. And that was a long time back. Right now django is ahead of rails in everything but hype. And one very important reason is i18n – outside the english speaking countries, rails just cannot compete because of this. So no point asking people to take the ‘best’ from each framework in order to wage war against a paper tiger. It’s not going to happen anyway.

About Me

I'm Patrick Thomson. This was a blog about computer programming and computer science that I wrote in high school and college. I have since disavowed many of the views expressed on this site, but I'm keeping it around out of fondness.

If you like this, you might want to check out my Twitter or Tumblr, both of which are occasionally about code.

Blog Stats

  • 695,966 hits

%d bloggers like this: