The disenchantment of Python web frameworks

tl;dr Popular Python web frameworks have less significant differences than it appears. Then, there’s Django which makes all competition look micro. Even given the rising popularity of FastAPI, I strongly believe there’s room for at least one another big framework.

Comparing Python frameworks

Back when I worked for a software house – STX Next I contributed several times to an article on their blog entitled A Beginner’s Introduction to Python Web Frameworks. The goal there was to provide a rough overview of available solutions. During work for the same company, I got hands-on experience with Django, Flask and Pyramid. In my opinion, Flask and Pyramid are quite alike. Others that I’ve been using for shorter periods of time are Sanic, aiohttp and Falcon. Currently, I’m working mostly with FastAPI and graphene.

How does the process of learning a new framework looks like? “Okay, here’s some hello world tutorial that shows how to manage routing, views, etc.”

Although the syntax may differ, for the most of the time the end result is exactly the same.

If one needs anything else, their best bet would be to type [name of framework]-[3rd party lib / missing function] into Google:

  • aiohttp-jwt
  • flask-sqlalchemy
  • flask-login
  • pyramid-openapi3

A common denominator for them is to leave design decisions up to developers – no imposed ORM or even type of databases, etc. There’s huge freedom in how to arrange code and your logic. Also, there are countless ways to do it wrong.

The most recent game-changer is having async support. I’m glad to see that new frameworks have it and existing ones (e.g. Flask or Django) are adding it. So it would be safe to assume that in a near future it will be available at hand. The other significant difference is what’s included. One hast to mention FastAPI that skillfully combines several libraries and Starlette framework to provide superior developer experience. I believe that prior to FastAPI, the only option to get such automatically updating documentation was to manually integrate several smaller libraries.

And then, there’s Django.

Django is a state of mind

Paraphrase of “Mac is a state of mind” from 2007 “Mac or PC” Rap Music Video

It has an order of magnitude more “goodies” bundled. It could be also characterised as opinionated. I don’t see it as a bad thing – but it’s a trait that cannot be ignored. To me, Django is Python’s Ruby On Rails. The most apparent similarity is their approach to persistence – both rely on the Active Record pattern. Django ORM sounds more familiar, but it’s just a reincarnation of an old pattern that says “attach save method to the model!”.

It’s so appealing, that this concept has been copied in the Python world several times – Masonite’s Orator, async-ready Tortoise ORM or peewee.

Beyond frameworks

To sum up, you’re either left to Django that makes a lot of decisions for you or a microframework where you have to figure out many things on your own. Microframeworks can provide building blocks (such as blueprint in Flask or router in FastAPI) but you can arrange them as you see fit. It’s a completely different experience from Django that has startapp CLI command.

In the longer term, what makes sense is how the project was divided (not with what means), how well it is tested or how easy it is for newcomers to grasp the overall structure. Of course, that counts if and only if the project is successful and has a chance to grow old.

My ideal framework

In my opinion, in Python’s ecosystem there’s a room for at least one more framework. It should have:

  • first-citizen dependency injection container (no, not like in FastAPI – not bound to views),
  • async support,
  • an ORM as powerful as SQLAlchemy (or the SQLAlchemy itself) but as an option,
  • testability from the ground up, starting from test client to tips on making app testable in parallel using pytest-sdist,
  • basic web framework stuff such as routes, views, etc,
  • GraphQL support or tips on integrating it would be nice,
  • + other boring stuff like configuration supporting several mechanisms etc

What can you have today from this list?

As far as I know, there are no frameworks in Python that would have all these features. My closest bet is to create one’s own mix using:

  • injector (no async support 🙁 – but there’s also Dependency Injector)
  • some configuration lib, which will be convenient with your deployment environment,
  • Starlette
  • SQLAlchemy (with recent advancements in supporting async it’s really promising!)
  • pytest + pytest-sdist + python-mockito + a few others

Summary

This post is mostly an old man’s rant, but the point is simple – don’t sweat it when choosing the next framework. Choose what seems to be the most comfortable in the mid-term (e.g. built-in features), but consider what it will take to support the solution in the long-term.

1

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.