I began my career in web application development by teaching myself Plone and Python for a client who requested some customizations of their Plone-based extranet back in 2004. The early aughts were the heyday of Plone, the enterprise grade open source content management system that is built on top of the Zope web application server. Zope is a fairly complex beast with its own object persistence solution called the Zope Object Database, or ZODB, which is like a dict of dicts, pickling Python objects and storing them directly on the filesystem. Zope has a steep learning curve. Zope is comprised of many modules and utilizes aspect oriented programming patterns through the use of utilities, interfaces and adapters in what is termed the Zope Component Architecture, or ZCA. Zope is configured using a dialect of XML known as ZCML, the Zope Configuration Markup Language. The layers of complexity and abstraction present in Zope and by extension Plone forced me to learn a lot about software engineering relatively rapidly in order to be productive. Over the next several years, I became more and more involved in the Plone community, attending conferences and sprints all over the world, and freelancing on a number of Plone based projects. The Plone community is one of the best open source communities in the world, hands down.
Then, in 2008, I met Django. Compared to Plone and Zope, Django has an elegant simplicity and relatively thin stack of code that is straightforward to customize. Lacking in much of the abstraction of the ZCA, Django’s learning curve is much less steep. Instead of using an object database, Django encourages the use of relational databases by default, and provides an object relational mapper (ORM) for querying these persistence mechanisms. I began learning more and more about Django and implementing client applications with it. When producing a simple web application that had no requirement for workflows and military grade security, I spent less time fighting the framework with Django than I did when using Plone. Plone is first and foremost a content management system, or CMS; a particular kind of web application geared toward managing content, with complex security and workflows built in. I soon came to the conclusion that Plone is well suited for projects requiring enterprise grade CMS functionality while Django is a better fit for general website development.
Somewhere along the way I decided to implement my own website using whatever framework I was currently interested in learning more about, so I took the Plone based site I had implemented and implemented a version using Django. The Django version of my site served me well, but my curiosity got the best of me and I longed to learn more about other Python web frameworks by reimplementing my site with another framework.
A good friend recommended Tornado. Tornado is a lightweight, asynchronous framework developed and open sourced by the folks at FriendFeed, prior to their acquisition by Facebook. It has a smaller footprint than Django, uses a non-blocking request handling model and has features similar to web.py, with Django-like templating. Batteries are not included and there is not as large of a community of Tornado developers to draw inspiration and advice from as the strong communities associated with Plone and Django. After implementing a basic version of my site using Tornado, I came to realize just how lightweight it really was. Tornado lacks some nice features found in other simple Python web frameworks. It was missing easy ways of handling 404 errors, for example. I was not running a high traffic site that could really benefit from an asynchronous request handling model. The benefits of non-blocking i/o did not outweigh the disadvantages presented by the lack of many niceties, and so I sought out other options in the Python web framework world.
There are many Python web frameworks from which to choose, and the number of options can be overwhelming to the uninitiated. Web2py, Cherrypy, Pylons, Turbogears, Bottle, Flask and Pyramid are some of the more commonly used frameworks, but this list is by no means exhaustive. I ended up choosing Pyramid for my next experiment, as I know the lead developer (Chris McDonough) personally, and know that he is an excellent programmer with many years of battle-tested Python knowledge under his belt, so I trusted that Pyramid would be well written, reliable and a pleasure to work with. I was right.
Pyramid is lightweight, its codebase is concise and readable, its documentation is quite good, its test coverage is excellent, and it has a robust set of extensions developed by its active community. Pyramid is a very configurable, flexible framework, with multiple data persistence, templating and application configuration options. The Pyramid author comes from the Zope world, and Pyramid’s design reflects this lineage. Pyramid makes extensive use of an architecture inspired by the ZCA under the hood, but developers utilizing the framework need not understand the ZCA in order to produce web applications using Pyramid. The default template engine chosen by the Pyramid developers, Chameleon, uses Zope Page Templates (ZPT). A Pyramid application can also be configured to render Mako or Jinja2 templates. The default persistence mechanism employed by Pyramid is the ZODB but it is straightforward to configure Pyramid to persist objects using the Sqlalchemy ORM or any of the various non-relational databases such as MongoDB. Pyramid’s default method of resolving URLs to code is traversal, but URL dispatch if you so desire. Pyramid applications are configured using ZCML, similar to Zope itself and also by writing Python code or via a declarative syntax in an .ini style text file. This flexibility is built in to almost every aspect of the code.
The flexibility provided by the Pyramid framework is both a boon and somewhat confusing, as a developer without strong opinions on persistence technologies and template technologies might not know which options to choose. One of the nicest things about Pyramid is that the framework makes it incredibly easy for a developer to get started on a project by providing various project templates that can be instantiated via invoking scripts. By simply running a script and passing in an argument indicating which project template to use, a developer is presented with an entire project structure wrapped in a Python package, complete with a working Pyramid app and unit test suite. Pyramid provides templates preconfigured for URL dispatch and Sqlalchemy, which makes it easy to choose options for your application other than the defaults.
After working with Pyramid for a few days and implementing a version of my website, I realized that I did not enjoy working with ZPT, so I tried Mako templates. I found I had grown too accustomed to Django-style templates to find Mako enjoyable, so I then decided to switch to Jinja2, which is very similar to Django templating, with some additional features and flexibility. I eventually decided that Pyramid is not the best fit for my own personal website, as I did not enjoy reconfiguring the default options to suit my needs, and my own personal site was so simple that the majority of Pyramid’s advanced features would not be utilized. I have no need for the Zope based technologies that Pyramid provides, and I wanted to explore other microframeworks. After researching Jinja2 further, I ended up reading up on the Jinja2 author’s (Armin Ronacher) microframework Flask. I was fascinated and wanted to learn more. I then set out to reimplement my site using Flask.
Flask is a breath of fresh air. Flask’s codebase is minuscule when compared with other Python web frameworks. It uses Jinja2 templates by default, is persistence agnostic, and uses the simple, reliable Werkzeug WSGI library for handling requests and responses to and from the application. The simplicity of Flask really appeals to me and there is a strong community of Flask developers writing plugins, example applications and snippets, which helps one become productive quite rapidly when employing this framework.
I implemented this site using Flask relatively quickly, and have enjoyed the simplicity and ease of use that Flask provides. Employing Flask makes developing web applications fun. This site is about 120 lines of code, and utilizes the Python database API and sqlite directly, rather than employing an ORM. Templating is a breeze with Jinja2, and handling things like 404 errors couldn’t be much simpler. I implemented markdown parsing with a two line diff. I implemented atom syndication based on a nice little snippet that Armin wrote demonstrating integrating Werkzeug’s atom feed generation API. All of the code fits neatly inside a single module which is easy to read and understand. The code for this site is available here on GitHub, if you are interested in the nuts and bolts. I am encouraged by my first experience developing a site with Flask and will definitely utilize this framework for more projects in the future.
I have explored a relatively wide range of Python web application frameworks and have come to the conclusion that there is no one perfect framework that suits every use case. Plone is great for implementing a CMS. Django is nice if you can leverage the admin interface or any of the many apps developed by its large open source community. Tornado is more suited towards sites looking for non-blocking i/o. Pyramid’s strength lies in its light weight and flexibility. Flask is very straightforward and ideally suited for rapid development of web applications. It is imperative that when developing a web application one utilizes the appropriate tool for the job. I recommend exploring the pluses and minuses of various Python frameworks before committing to any one in particular.