Review: 13 Python web frameworks compared

Python programmers have many excellent options for creating web apps and APIs; Django, Weppy, Bottle, and Flask lead the way

Review: 13 Python web frameworks compared
Mattes (CC BY-SA 3.0)

If you are developing a web application and you have picked Python as the language to build it in, that’s a smart move. Python’s maturity of development, robust libraries, and breadth of real-world adoption have helped make it a no-brainer for web development.

Now comes the hard part: Picking one of the many Python web frameworks available. It’s not only that the number keeps growing, but it can be hard to find the one that best fits your use case. If you’re constructing a quick-and-dirty REST API, you won’t need anywhere near the plumbing and wiring required for a full user-facing application with user logins, form validations, and upload handling.

Related video: Creating a simple web app with Python and Flask

In this roundup, we’ll examine 13 of the most widely deployed Python web frameworks. We’ll note what kinds of web applications each is best suited to building and look into how they stack up against one another in these six areas:

Installation: How easy or straightforward it is to set up the framework—projects that don’t require formal installation (it can simply be dropped into an existing project as an included module), require minimal boilerplate to get started, or come with some kind of preconfigured setup get extra points.

Documentation: Nearly every decent Python project has documentation that walks through setup, illustrates basic use cases, and provides details about the APIs. Here, we give higher marks to frameworks that show how to create an entire app as part of the tutorial, include common recipes or design patterns, and otherwise go above and beyond the call of duty (such as by providing details about how to run the framework under a Python variant like PyPy or IronPython).

Management: This is a relative score, indicating how much work is required to configure and maintain the framework. Minimal frameworks score higher here by default.

Native capabilities: How many batteries are included? Higher scores go to frameworks that provide native support for internationalization, HTML templating, and a data access layer. Points also go to frameworks that make native use of Python’s recently introduced native support for asynchronous I/O operations.

Security: Frameworks that provide native security measures like cross-site request forgery (CSRF) protection and session management with encrypted cookies get higher marks.

Scalability: Most Python frameworks can make use of projects like Gevent or Gunicorn to run at scale. Here, we look at features native to the framework that promote scalability, like output and page-fragment caching.

If you’re curious about performance benchmarks, take a look at TechEmpower’s ongoing series of trials, which compare multiple web frameworks across various tasks, with code and methodologies posted to GitHub and subjected to constant reassessment. Not all of the frameworks in this discussion are analyzed there, but it’s possible to get a good sense of which frameworks perform best under what kinds of loads.

We’ll look at 13 frameworks in all. Five of these—CubicWeb, Django, Web2py, Weppy, and Zope2—take the “kitchen sink” approach, packing in most every feature you could imagine needing for a web application. The remaining eight frameworks—Bottle, CherryPy, Falcon, Flask, Pyramid, Tornado,, and Wheezy.web—offer a more minimalist take, trading bulk and completeness for simplicity and ease.

Let’s start with the heavyweights.

Heavyweight Python web frameworks


CubicWeb is billed as “a semantic web application framework that favors reuse and object-oriented design.” It’s an intriguing system—as noted by Rick Grehan when he looked at it for InfoWorld back in 2011—that emphasizes the use of abstractions and reusable building blocks of code called “cubes,” but it might be too abstract or idiosyncratic for some developers.

Cubes are software components that feature a schema (data model), entities (programming logic), and views. By assembling multiple cubes, each performing its own task, you can compose software applications by reusing your own code and the code of others.

At its core, CubicWeb provides basic scaffolding used by every web app: a “repository” for data connections and storage; a “web engine” for basic HTTP request/response and CRUD actions; and a schema for modeling data. All of this is described in Python class definitions. To set up and manage instances of CubicWeb, you work with a command-line tool similar to the one used for Django.

CubicWeb does not appear to use Python 3’s native async functionality. A roundabout way to include async would be to use the cubicweb.pyramid module to use the Pyramid framework as the web server, and draw on a fork of Pyramid that uses async constructions. But anything more straightforward seems out of reach for now.

To fetch or manipulate persistent data in a CubicWeb app, you use Relation Query Language (RQL), which employs vaguely SQL-like syntax but is patterned after the W3C’s SparQL. CubicWeb’s justification for this is, again, abstraction: RQL provides a highly decoupled route to interrelate various data sources. But as it’s implemented, by manually constructing queries as strings, it’ll likely feel antiquated to developers accustomed to ORMs.

There are other obstacles to using CubicWeb. For one, setup can be a hassle. Because CubicWeb has a lot of dependencies, it’s best to use pip install to fetch them all. You may also have to perform a certain amount of manual tweaking on the local environment. This is in stark contrast to other frameworks where running pip install or dropping the framework’s code into a subfolder of another project is all that’s required.

Another potential issue is the absence of a native template engine; generating HTML is left to the developer. You could overcome this by using a third-party templating system like Jinja2 or opting for a cube that provides tools for Web UIs, such as that for the Boostrap HTML framework.

One long-standing issue with CubicWeb — the lack of Python 3 support — has been resolved. As of June 2016 and version 3.23, Python 3 support landed in CubicWeb, except for modules like Twisted that aren’t themselves fully ported.

Like Web2py, CubicWeb refers to its lengthy documentation as “the book.” It takes the time to explain CubicWeb’s unusual approach, demonstrates how to build some basic applications, includes API references, and in general goes out of its way to be specific.


In the decade and change since Django first appeared, it has become one of Python’s most widely deployed frameworks for creating web applications. Django comes with most every battery you might need, so it leans more toward building big applications than small ones.

Related video: Creating a simple website with Django

After many years of sitting at version 1.x, Django recently made a version bump to the left of the decimal point. The biggest change in Django 2.0 is that the framework now works only with Python 3.4 and up. Ideally, you should use Python 3.x anyway, so the only reason to use the 1.x branch of Django is if you’re stuck with an old version of Python.

A key part of Django’s appeal is deployment speed. Because it includes so many pieces you need for developing the average web application, you can get moving quickly. Routing, URL parsing, database connectivity (including an ORM), form validation, attack protections, and templating are all built in.

You’ll find building blocks for most common web application scenarios. User management, for instance, is found on most websites, so Django offers it as a standard element. Instead of having to create your own system for tracking user accounts, sessions, passwords, logins/logouts, admin permissions, and so on, Django has those features natively. They can be used as-is or extended to encompass new use cases with a minimal amount of work.

Django has sane and safe defaults that help shield your web application from attack. When you place a variable in a page template, such as a string with HTML or JavaScript, the contents are not rendered literally unless you explicitly designate the instance of the variable as safe. This by itself cuts down on many common cross-site scripting issues. If you want to perform form validation, you can use everything from simple CSRF protection to full-blown field-by-field validation mechanisms that return detailed error feedback.

A feature set as rich and broad as Django’s wouldn’t be much good without robust documentation to go with it. Django’s documentation site drills into every aspect of the framework from multiple angles. Working with Python 3 or other flavors of the language, doing security right, implementing common web application components (like sessions or pagination), generating sitemaps—they’re all covered. The APIs for each layer of the application—model, view, and template—are described in detail as well.

With great power, however, comes great complexity. Django applications have a reputation for being top-heavy, with many moving parts. Even a simple Django app with only a couple of routes requires a fair amount of configuration to get running. If your job is to do nothing more than set up a couple of simple REST endpoints, Django is almost certainly overkill.

Django also has its quirks. For instance, page templates cannot use callables. Example: You can pass {{}} as a component in a template, but not {{user.get_name()}}. It’s one of the ways Django ensures templates don’t inadvertently do nasty things, but those constraints can be jarring if you’re not prepared for them. While there are workarounds, they tend to take a toll on performance.

Django’s core is synchronous. However, one way to add async behavior is by way of the Django Channels project. This project, an official Django add-on, adds async handling for connections and sockets to Django, while preserving Django’s programming idioms.


In the Ruby world, Ruby on Rails is the de facto web framework. DePaul University computer science professor Massimo Di Pierro was inspired by Rails to create a web framework in Python that was similarly easy to set up and work with. The result is Web2py.

Web2py’s biggest attraction is its built-in development environment. When you set up an instance of Web2py, you’re provided with a web interface, essentially an online Python application editor, where you can configure the app’s components. This typically means creating models, views, and controllers, each described via Python modules or HTML templates. A few example apps come with Web2py out of the box. You can take those apart to see how they work or leverage them as starter templates to create your own apps.

Developers typically deploy Web2py by simply downloading its source code and using that. But for less technical users on Windows or MacOS, Web2py’s creators offer versions that are essentially standalone servers. Download, unpack, and run one of these versions, and you’ll have a local web server with a preconfigured copy of Web2py built in. This is a nice way to get a leg up on creating a Web2py app, which can then be deployed elsewhere as needed.

Web2py’s web interface was built with Bootstrap 2.16.1, so it’s easy on the eyes and easy to navigate. The in-browser editor is no substitute for a full-blown IDE, but it’s outfitted with helpful aids like line numbering and Python syntax highlighting (including auto-indentation). Also included is a quick web interface to the Python shell, so you can interact with Web2py from the command line if needed—a nice concession to experts.

The data abstraction system used in Web2py works a little differently from Django’s ORM and other ORMs inspired by it (such as Peewee). Those systems use Python classes to define models, where in Web2py you use constructor functions like define_table to instantiate models. Most of those differences are likely to be jarring only to people who already have experience with one and are starting to use the other; they’re about equally complex to newcomers. You’re not likely to have any trouble with hitching Web2py to a data provider, as it talks to nearly every major database in existence.

A truly useful database-related function is the ability to generate a diagram of the models, the better to visualize how your models relate to each other. You will need to install the pygraphviz library to enable that feature, though.

Web2py supplies many other professional-grade components: internationalization functions, multiple caching methodologies, access control and authorization, and even front-end effects (for example, a date picker in forms) via integrated support for jQuery and AJAX. Hooks for external and internal middleware are also included, although you aren’t allowed to use middleware to replace core Web2py functions.

One significant limitation of Web2py is that it is compatible with Python 2.x only. For one, this means Web2py can’t make use of Python 3’s async syntax. For two, if you rely on external libraries that are exclusive to Python 3, then you’re out of luck. However, work is under way to make Web2py Python 3 compliant, and it’s very near completion as of this writing.

It’s no wonder that Web2py’s documentation is referred to as “the book.” First, it covers a staggering amount of material on Web2py, Python, and the deployment environments used for both. Second, it’s written in a highly accessible, narrative style. Third, it talks in-depth about common application-building scenarios. There’s an entire chapter, for instance, on using jQuery (bundled with Web2Py) to build AJAX applications.


Weppy feels like a halfway mark between the minimal simplicity of Flask and the completeness of Django. While developing a Weppy app has the straightforwardness of Flash, Weppy comes with many features found in Django, like data layers and authentication. Thus, Weppy is suited to apps that range from extremely simple to modestly sophisticated.

At first glance, Weppy code looks a great deal like Flask or Bottle code. Few instructions are needed to get a basic, single-route website up and running. Routes can be described through function decorators (the easy way) or programmatically, and the syntax for doing so hews closely to Flask/Bottle. Templating works about the same, aside from minor variations in syntax.

Weppy contrasts with those other frameworks by including some features they incorporate only as plug-ins or add-ons. For instance, neither Flask nor Bottle has a built-in ORM or a data management system. Weppy includes an ORM, albeit one based on the pyDAL project rather than the far more popular SQLAlchemy. Weppy even supports schema migrations, which Django supports as part of its ORM (also, Django’s migration system is a great deal more automated). While Weppy has an extension mechanism, the list of officially approved add-ons is tiny, far smaller than the catalog of extensions for Flask.

Lighter-weight frameworks like Weppy are often used to build RESTful APIs, and Weppy comes outfitted with convenience functions for that purpose. Put a @service decorator on a route, and the data you return is automatically formatted in your choice of JSON or XML.

Weppy includes other features that seem more in line with a larger framework, but they’re implemented without bulk. Examples: Data validation mechanisms, form handling, response caching, and user validation. In all of these cases, Weppy takes a “just enough” approach. The features provided aren’t as complete as you might find in a Django-sized framework, but a developer doesn’t need to invest a lot of work in making them useful, and they can always be extended after the fact.

Another feature found in Weppy typically associated with a more heavyweight framework is internationalization support. Strings in templates can be translated according to locale files provided with the application, which are simple Python dictionaries. The choice of language can also be set by parsing the browser request (that is, the Accept-Language HTTP header) or by binding a translation to a specific route.

Weppy’s documentation has the same flavor as the framework itself. It’s clean, readable, and written to be consumed by humans. Aside from the usual “hello world” app example, it includes a nice walkthrough tutorial that lets you create a microblogging system as a starter project.

Long-term plans for Weppy include supporting async and sockets as low-level, first-class entities. Weppy’s developers plan to introduce those features in version 2.0, and then to require Python 3.7 or better for all future versions of Weppy.

InfoWorld Scorecard
Native capability (20%)
Management (20%)
Installation (20%)
Documentation (20%)
Security (10%)
Scalability (10%)
Overall Score (100%)
Bottle 0.12 8 10 10 8 7 7 8.6
CherryPy 17.0.0 7 9 9 9 8 8 8.4
CubicWeb 3.26.4 10 8 7 10 9 7 8.6
Django 2.1 10 8 8 10 10 10 9.2
Falcon 1.4.1 7 10 8 8 7 7 8.0
Flask 1.0.2 8 9 8 9 8 8 8.4
Pyramid 1.9.2 8 8 8 10 9 7 8.4
Tornado 4.3 8 9 9 8 8 7 8.3 0.39 8 8 10 8 9 8 8.5
Web2py 2.16.1 10 9 7 10 9 8 8.9
Weppy 1.2.11 10 8 9 9 10 9 9.1
Wheezy.web 0.1.485 9 9 8 8 8 8 8.4
Zope2 2.13.24 10 8 7 9 9 9 8.6
1 2 Page 1
Page 1 of 2