Design Decisions in Flask

If you are curious why Flask does certain things the way it does and notdifferently, this section is for you. This should give you an idea aboutsome of the design decisions that may appear arbitrary and surprising atfirst, especially in direct comparison with other frameworks.

The Explicit Application Object

A Python web application based on WSGI has to have one central callableobject that implements the actual application. In Flask this is aninstance of theFlask class. Each Flask application hasto create an instance of this class itself and pass it the name of themodule, but why can't Flask do that itself?

Without such an explicit application object the following code:

fromflaskimportFlaskapp=Flask(__name__)@app.route('/')defindex():return'Hello World!'

Would look like this instead:

fromhypothetical_flaskimportroute@route('/')defindex():return'Hello World!'

There are three major reasons for this. The most important one is thatimplicit application objects require that there may only be one instance atthe time. There are ways to fake multiple applications with a singleapplication object, like maintaining a stack of applications, but thiscauses some problems I won't outline here in detail. Now the question is:when does a microframework need more than one application at the sametime? A good example for this is unit testing. When you want to testsomething it can be very helpful to create a minimal application to testspecific behavior. When the application object is deleted everything itallocated will be freed again.

Another thing that becomes possible when you have an explicit object lyingaround in your code is that you can subclass the base class(Flask) to alter specific behavior. This would not bepossible without hacks if the object were created ahead of time for youbased on a class that is not exposed to you.

But there is another very important reason why Flask depends on anexplicit instantiation of that class: the package name. Whenever youcreate a Flask instance you usually pass it__name__ as package name.Flask depends on that information to properly load resources relativeto your module. With Python's outstanding support for reflection it canthen access the package to figure out where the templates and static filesare stored (seeopen_resource()). Now obviously thereare frameworks around that do not need any configuration and will still beable to load templates relative to your application module. But they haveto use the current working directory for that, which is a very unreliableway to determine where the application is. The current working directoryis process-wide and if you are running multiple applications in oneprocess (which could happen in a webserver without you knowing) the pathswill be off. Worse: many webservers do not set the working directory tothe directory of your application but to the document root which does nothave to be the same folder.

The third reason is "explicit is better than implicit". That object isyour WSGI application, you don't have to remember anything else. If youwant to apply a WSGI middleware, just wrap it and you're done (thoughthere are better ways to do that so that you do not lose the referenceto the application objectwsgi_app()).

Furthermore this design makes it possible to use a factory function tocreate the application which is very helpful for unit testing and similarthings (アプリケーション製造工場(Application Factories)).

The Routing System

Flask uses the Werkzeug routing system which was designed toautomatically order routes by complexity. This means that you can declareroutes in arbitrary order and they will still work as expected. This is arequirement if you want to properly implement decorator based routingsince decorators could be fired in undefined order when the application issplit into multiple modules.

Another design decision with the Werkzeug routing system is that routesin Werkzeug try to ensure that URLs are unique. Werkzeug will go quite farwith that in that it will automatically redirect to a canonical URL if a routeis ambiguous.

One Template Engine

Flask decides on one template engine: Jinja2. Why doesn't Flask have apluggable template engine interface? You can obviously use a differenttemplate engine, but Flask will still configure Jinja2 for you. Whilethat limitation that Jinja2 isalways configured will probably go away,the decision to bundle one template engine and use that will not.

Template engines are like programming languages and each of those engineshas a certain understanding about how things work. On the surface theyall work the same: you tell the engine to evaluate a template with a setof variables and take the return value as string.

But that's about where similarities end. Jinja2 for example has anextensive filter system, a certain way to do template inheritance,support for reusable blocks (macros) that can be used from insidetemplates and also from Python code, supports iterative templaterendering, configurable syntax and more. On the other hand an enginelike Genshi is based on XML stream evaluation, template inheritance bytaking the availability of XPath into account and more. Mako on theother hand treats templates similar to Python modules.

When it comes to connecting a template engine with an application orframework there is more than just rendering templates. For instance,Flask uses Jinja2's extensive autoescaping support. Also it providesways to access macros from Jinja2 templates.

A template abstraction layer that would not take the unique features ofthe template engines away is a science on its own and a too largeundertaking for a microframework like Flask.

Furthermore extensions can then easily depend on one template languagebeing present. You can easily use your own templating language, but anextension could still depend on Jinja itself.

What does "micro" mean?

“Micro” does not mean that your whole web application has to fit into a singlePython file (although it certainly can), nor does it mean that Flask is lackingin functionality. The "micro" in microframework means Flask aims to keep thecore simple but extensible. Flask won't make many decisions for you, such aswhat database to use. Those decisions that it does make, such as whattemplating engine to use, are easy to change. Everything else is up to you, sothat Flask can be everything you need and nothing you don't.

By default, Flask does not include a database abstraction layer, formvalidation or anything else where different libraries already exist that canhandle that. Instead, Flask supports extensions to add such functionality toyour application as if it was implemented in Flask itself. Numerous extensionsprovide database integration, form validation, upload handling, various openauthentication technologies, and more. Flask may be "micro", but it's ready forproduction use on a variety of needs.

Why does Flask call itself a microframework and yet it depends on twolibraries (namely Werkzeug and Jinja2). Why shouldn't it? If we lookover to the Ruby side of web development there we have a protocol verysimilar to WSGI. Just that it's called Rack there, but besides that itlooks very much like a WSGI rendition for Ruby. But nearly allapplications in Ruby land do not work with Rack directly, but on top of alibrary with the same name. This Rack library has two equivalents inPython: WebOb (formerly Paste) and Werkzeug. Paste is still around butfrom my understanding it's sort of deprecated in favour of WebOb. Thedevelopment of WebOb and Werkzeug started side by side with similar ideasin mind: be a good implementation of WSGI for other applications to takeadvantage.

Flask is a framework that takes advantage of the work already done byWerkzeug to properly interface WSGI (which can be a complex task attimes). Thanks to recent developments in the Python packageinfrastructure, packages with dependencies are no longer an issue andthere are very few reasons against having libraries that depend on others.

Thread Locals

Flask uses thread local objects (context local objects in fact, theysupport greenlet contexts as well) for request, session and an extraobject you can put your own things on (g). Why is that andisn't that a bad idea?

Yes it is usually not such a bright idea to use thread locals. They causetroubles for servers that are not based on the concept of threads and makelarge applications harder to maintain. However Flask is just not designedfor large applications or asynchronous servers. Flask wants to make itquick and easy to write a traditional web application.

Async/await and ASGI support

Flask supportsasync coroutines for view functions by executing thecoroutine on a separate thread instead of using an event loop on themain thread as an async-first (ASGI) framework would. This is necessaryfor Flask to remain backwards compatible with extensions and code builtbeforeasync was introduced into Python. This compromise introducesa performance cost compared with the ASGI frameworks, due to theoverhead of the threads.

Due to how tied to WSGI Flask's code is, it's not clear if it's possibleto make theFlask class support ASGI and WSGI at the same time. Workis currently being done in Werkzeug to work with ASGI, which mayeventually enable support in Flask as well.

Seeasync と await の使用 for more discussion.

What Flask is, What Flask is Not

Flask will never have a database layer. It will not have a form libraryor anything else in that direction. Flask itself just bridges to Werkzeugto implement a proper WSGI application and to Jinja2 to handle templating.It also binds to a few common standard library packages such as logging.Everything else is up for extensions.

Why is this the case? Because people have different preferences andrequirements and Flask could not meet those if it would force any of thisinto the core. The majority of web applications will need a templateengine in some sort. However not every application needs a SQL database.

As your codebase grows, you are free to make the design decisions appropriatefor your project. Flask will continue to provide a very simple glue layer tothe best that Python has to offer. You can implement advanced patterns inSQLAlchemy or another database tool, introduce non-relational data persistenceas appropriate, and take advantage of framework-agnostic tools built for WSGI,the Python web interface.

The idea of Flask is to build a good foundation for all applications.Everything else is up to you or extensions.