A web framework is a so-called library that makes a developer’s life easier when building web applications. Most popular web frameworks encapsulate what developers across the globe have learnt over the past twenty years.

Most of you, when choosing a framework for a web application, firstly think of Django. No doubt, in lots of cases Django, with its everything-out-of-the-box and (killer feature) automatic database generation, is very suitable.

However, let's take a look around. There is a number of various Python frameworks and, surprisingly, they aren't clones of Django. Some of them are surely better to choose, or at least worth learning, for some specific usage.

There are more than 25 active Python web framework projects. Projects without stable releases during the last 365 days and web frameworks not supporting Python 3 were left out.

Let's develop a very simple application with each popular framework. An application must response "Hello world!" to /hello/ request, and also templating must be used to pass "Hello world!" message dynamically. Database access is another important question regarding frameworks, but comparison of that would be too bulky for such brief overview. For API frameworks (Falcon, Hug and Sanic) a response must be {'message': 'Hello world!'}.

The comparison of Python web frameworks includes

Another Python web frameworks comparison includes

Django

Github stars: 30164
Github forks: 12712

The main principle of Django ninjas is to develop everything of any complexity IN TIME. It's originally developed for content-management systems, but its rich features including but not limited to templating, automatic database generation, DB access layer, automatic admin interface generation - are well suitable for other kinds of web applications. Provides a web server for development use. Form serialization and validation, template system with inheritance of templates, caching in several ways, internationalization, serialization to XML or JSON. Django is contributed with applications for maintenance: an authorization system, a dynamic admin system, RSS and Atom generators, Google Sitemaps generator, and so on. Django is used by Instagram, Pinterest, The Washington Times, Disqus, Bitbucket and Mozilla.

When developing a microapplication, Django begins with complexity from the start. After installing the package (Django, literally), you must create a project with "django-admin startproject myproject" command. Then, you should configure the app in myproject/myproject/settings.py - at least database access and templating. Django application is always unified in structure. It's excellent when you develop many boring applications. My application is:

myproject/myproject/views.py

from django.shortcuts import render

def hello(request):
    return render(request, 'myproject/message.html', {'message': 'Hello world!'})

myproject/myproject/urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^hello/', views.hello, name='hello'),
]

Also, there are some obvious changes in myproject/myproject/settings.py and the template in myproject/templates/myproject/message.html.

pip freeze contains: appdirs, Django, packaging, pyparsing, six.

Official website | GitHub page | PyPI page

Flask

Github stars: 31543
Github forks: 9936

A microframework for Python based on Werkzeug and Jinja2 "with good intentions". Being a microframework, Flask is worth using when developing small applications with simple requirements, not like Django, Pyramid, et cetera. For example, you may operate your database in any way you like with Flask - by means of SQLAlchemy or anything else. Use it if you are going to develop a small application and configure everything by yourself. Other features: development web server, integrated unit testing support, Google App Engine compatibility. Flask is used by LinkedIn, Pinterest.

Flask example is very simple.

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/hello")
def hello():
    return render_template('message.html', message="Hello world!")

if __name__ == "__main__":
    app.run()

pip freeze contains: appdirs, click, Flask, itsdangerous, Jinja2, MarkupSafe, packaging, pyparsing, six, Werkzeug.

Official website | GitHub page | PyPI page

Tornado

Github stars: 14741
Github forks: 4349

Its main feature is non-blocking I/O. Thus, Tornado can be scaled to handle tens of thousands of open connections. An ideal framework for long polling, WebSockets and other usages with continuous connection. Tornado officially supports only Linux and BSD OS (Mac OS X and Microsoft Windows are recommended only for development use). The origin of Tornado is FriendFeed project, now owned by Facebook.

The task doesn't need any of the Tornado's key features - asynchrony. By the way, simple applications with Tornado are easy.

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render('message.html', message='Hello world!')

def make_app():
    return tornado.web.Application([
        (r"/hello", MainHandler),
    ])

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

pip freeze lists: appdirs, packaging, pyparsing, six, tornado.

Official website | GitHub page | PyPI page

Falcon

Github stars: 4587
Github forks: 487

Falcon is a microframework for small applications, app backends and higher-level frameworks. It encourages to follow the REST concept, and thus, you should think of resources and state transitions mapped to HTTP methods when developing with Falcon. Falcon is one of the fastest web frameworks in Python. It's used by Cronitor, EMC, Hurricane Electric, OpenStack, Opera Software, Wargaming, Rackspace.

Falcon is not suitable for serving HTML pages at all. It is appropriate for RESTful APIs. Here is some code to response for a GET request with a JSON response.

import falcon
import json

class HelloResource:
    def on_get(self, request, response):
        """Handles GET requests"""
        data = {'message': 'Hello world!'}

        response.body = json.dumps(data)

api = falcon.API()
api.add_route('/hello', HelloResource())

As Falcon doesn't include a server, Waitress is suitable as one. waitress-serve --port=8080 app:api

The response for http://localhost:8080/hello is simple {"message": "Hello world!"}

pip freeze contains: appdirs, falcon, packaging, pyparsing, python-mimeparse, six, waitress.

Official website | GitHub page | PyPI page

Hug

Github stars: 4793
Github forks: 252

One of the fastest web frameworks for Python. It's designed to build APIs. It supports providing of several API versions, automatic API documentation and annotation-powered validation. Also, hug is built on top of another JSON framework which is Falcon. Let's provide a JSON response via hug.

import hug

@hug.get()
@hug.local()
def hello():
    return {'message': 'Hello world!'}

Launched with the waitress by command "waitress-serve --port=8080 app:__hug_wsgi__"

pip freeze contains: appdirs, falcon, hug, packaging, pyparsing, python-mimeparse, requests, six, waitress.

Official website | GitHub page | PyPI page

Sanic

Github stars: 7462
Github forks: 406

A Flask-like web framework which is developed to be fast. It supports asynchronous request handlers, making your code non-blocking and speedy.

Code to response with {'message': 'Hello world!'} JSON may be seen below.

from sanic import Sanic
from sanic.response import json

app = Sanic()

@app.route("/hello")
async def hello(request):
    return json({"message": "Hello world!"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

pip freeze lists: aiofiles, appdirs, httptools, packaging, pyparsing, sanic, six, ujson, uvloop.

GitHub page | PyPI page

aiohttp

Github stars: 4184
Github forks: 733

Aiohttp is an asynchronous web framework which heavily utilizes Python 3.5+ async & await features. An example below shows only an obvious non-asynchronous functionality. aiohttp is not just a server web framework, but also the client one. It supports both WebSocket server and client. As it supports integration with Jinja2, the example uses this feature.

from aiohttp import web
import aiohttp_jinja2
import jinja2

@aiohttp_jinja2.template('message.html')
def hello(request):
    return {'message': 'Hello world!'}

app = web.Application()
aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('templates'))
app.router.add_get('/hello', hello)

web.run_app(app, port=8080)

pip freeze lists: aiohttp, aiohttp-jinja2, appdirs, async-timeout, cchardet, chardet, Jinja2, MarkupSafe, multidict, packaging, pyparsing, six, yarl.

Official website | GitHub page | PyPI page

Pyramid

Github stars: 2566
Github forks: 773

A framework for large applications. It aims to be flexible, unlike "everything-in-the-box" Django. For example, templating and database administration require external libraries. Pyramid web applications start from a single-file module and evolve into ambitious projects.

It took time to develop a single file application with Pyramid! Pyramid docs are cheating: if you want to response with a string, you have an example on their home page, but when you attempt to use templating... Docs are unclear, firstly because of unobvious proposed project structure. Pyramid_chameleon has been additionally installed to use Chameleon templating (${} instead of Jinja's {{}}). Here is the code of the application.

from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.renderers import render_to_response


def hello(request):
    return render_to_response('__main__:templates/message.pt',
                              {'message':'Hello world!'},
                              request=request)

if __name__ == '__main__':
    config = Configurator()
    config.include('pyramid_chameleon')
    config.add_route('hello', '/hello')
    config.add_view(hello, route_name='hello')
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 6543, app)
    server.serve_forever()

pip freeze lists: appdirs, Chameleon, hupper, packaging, PasteDeploy, pyparsing, pyramid, pyramid-chameleon, repoze.lru, six, translationstring, venusian, WebOb, zope.deprecation, zope.interface.

Frameworks below are less popular. However, as they are still being developed, it's good to mention them.

Official website | GitHub page | PyPI page

Growler

Github stars: 692
Github forks: 27

Built atop asyncio, inspired by Connect and Express frameworks for Node.js. ORM, templating, etc. should be installed manually. Requests are handled by passing through a middleware chain.

GitHub page | PyPI page

CherryPy

Github stars: 557
Github forks: 151

Aims to be "a way between the programmer and the problem". A usual web application developed by means of CherryPy looks like an ordinary Python application, it works out of the box without a complicated setup and customization. Also, it supports different web servers like Apache, IIS, et cetera. CherryPy contains an embedded web-server, so your application may be deployed anywhere where Python is installed. CherryPy allows to launch multiple HTTP servers at once. Output compression, configurability of every part, flexible plugin system. CherryPy doesn't force you to use any certain template engine, ORM or JavaScript library, so you may use what you prefer.

Official website | GitHubPage | PyPI page

MorePath

Github stars: 340
Github forks: 35

A flexible model-driven web framework. Supports REST out-of-the-box. Its main concepts are reusability and extensibility.

Official website | GitHub page | PyPI page

TurboGears2

Github stars: 228
Github forks: 54

A MVC web framework.The Features include ORM with real multi-database support, support for horizontal data partitioning, widget system to simplify the development of AJAX apps. Template engine is Kajiki (must be additionally installed). You can develop with TurboGears as it's a microframework as well as it's a full-stack solution, installing additional components. Notice that TurboGears2's PyPI package is called tg.devtools.

Official website | GitHub page | PyPI page

Circuits

Github stars: 152
Github forks: 40

Circuits has similar features as CherryPy has. Unlike CherryPy, circuits are a highly efficient web framework for developing stand-alone multiprocess applications. It's event-driven, it supports concurrency, asynchronous I/O components. It's fully usable out-of-the-box.

Official website | GitHub page | PyPI page

Watson-framework

Github stars: 88
Github forks: 10

A component-based WSGI event-driven MVC web framework.

GitHub page | PyPI page

Pycnic

Github stars: 108
Github forks: 19

One of the fastest web frameworks for Python to develop JSON APIs.

Official website | GitHub page | PyPI page

WebCore

Github stars: 66
Github forks: 8

A light-weight full-stack framework. You can develop an application in a single file or structurize it as you wish.

GitHub page | PyPI page

Reahl

Github stars: 10
Github forks: 3

A web framework to develop web applications in pure Python. There are widgets which may be used, customized and composed in usual Python code. Those widgets describe a specific server-side and client-side behaviour.

Official pag | GitHub page | PyPI page

The following diagrams present the number of forks and stars on GitHub:

number of forks
number of stars

Summary

This concise overview leaves the best Python frameworks at your disposal. Try aiohttp or Tornado if you need asynchrony, when you develop something with continuous connections.

Try Django if you develop something rich.

Try Falcon, hug or Sanic if you develop JSON API.

Try Flask if you develop something simple.

Try Pyramid if you develop something rich, but extraordinary.

We can’t single out the best Python framework for web development since the choice of either framework depends on the specific needs of a project. Learn about the top 10 Python frameworks in 2020 and have different tools for different tasks in your toolbox.