The world of web frameworks never stays still for long, and Python is lucky to have several strong options.
Django recently reached version 2.0. It's fairly remarkable, since version 1.0 was released in late 2008 - almost a decade ago!
The slow versioning is misleading though. The project is very active, with commits being made on Github daily. People who've had early experience with Django, in say version 1.4, will be amazed by the improvements, particularly to the ORM.
Version 2.0 just supports Python 3. This is undoubtedly a benefit, and will result in a leaner and cleaner code base.
It also opens up interesting design possibilities, as Python 3 has some notable new features, which other frameworks are using in interesting ways:
The URL routing syntax has also been cleaned up, and doesn't rely on users writing regular expressions anymore.
It sounds like an insult to call a framework boring, but that's what Django is now.
It's battle hardened, and won't blow up in your face. A lot of developers have used it, so getting help shouldn't be hard.
The approach that Django took was 'batteries included'. There are lots of modules within Django for solving common problems. For example, there are modules for auth, GIS, and the ever popular admin screen.
You can be confident that anything within the Django codebase will work well. If grabbing another auth module off the shelf, it might not work as flawlessly with Django as the one shipped with Django itself.
One often overlooked part of Django is how it encourages modularity.
A project consists of lots of apps. By structuring your code this way, it encourages a 'microservice' style approach. Apps can also be decoupled by using Django signals.
I've seen huge Django projects, consisting of dozens of apps.
No discussion of Django is complete without mentioning the admin screen.
It provides a nice web GUI for admin users to manage the web app's data. For many, this was the standout feature of Django, and continues to be indespensible. And with Django 2.0, it's responsive too, so can be used on mobile devices.
The long term popularity of Django has resulted in other supporting tools and frameworks which extend Django in interesting ways.
With Channels, you can support web sockets within your synchronous Django app.
This is a great option for creating APIs.
This is a simple framework. It follows the Unix philosophy of doing one thing well.
In constrast to Django, Flask just does the basics (routing and views), and relies on other packages to fill in the gaps.
It doesn't ship with an ORM, but people commonly use SQLAlchemy.
When using Flask, you're going to have to make some decisions. Which ORM do you want to use? How will you handle auth? What about forms?
This is the strength and weakness of Flask.
For people with experience in Python, they appreciate the flexibility, and know how the bits plug together. But for a newcomer, it can be overwhelming.
Anyone who works with Flask will tell you that the framework is very well structured. The way routing works is simple and intuitive, and has been cloned by other frameworks.
Django and Flask are the two largest synchronous frameworks.
Until recently, the most popular asynchronous frameworks were Tornado and Twisted.
With the addition of asyncio in Python 3, a bunch of new frameworks have emerged. The one which seems to be pulling ahead is Sanic.
Sanic is a fairly new framework which has a lot of similarities to Flask, but is asynchronous.
Sanic has far greater throughput than Django and Flask.
Many async frameworks have code which is difficult to understand. Sanic is very clean, and easy to follow.
Django and Flask aren't going anywhere soon. Raw performance isn't the only thing to consider when picking a framework, especially since most web apps don't require blistering speed.
However, for the cases where performance is important - it's good that Python can compete with other languages.