(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 web.py. 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
- 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.
- 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. - 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).
- 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
- 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.
- Make Ajax integration easy. Turbogears does this brilliantly with MochiKit; Django doesn’t do this at all. However, Rails still beats them both with Script.aculo.us tools.
- 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
- 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.
- 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.
- 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
- 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.
- Use Django’s admin interface. It’s slick, functional, and easy – keep it.
- 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.
- 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.
- 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.
- Learn from Turbogears’ extensibility. Using
easy_installto 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. - 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.
- 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.
- 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.