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 the Flask
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:
- from flask import Flask
- app = Flask(__name__)
- @app.route('/')
- def index():
- return 'Hello World!'
Would look like this instead:
- from hypothetical_flask import route
- @route('/')
- def index():
- 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 (see open_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 object wsgi_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 is always 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, supportfor reusable blocks (macros) that can be used from inside templates andalso from Python code, uses Unicode for all operations, supportsiterative template rendering, configurable syntax and more. On the otherhand an engine like Genshi is based on XML stream evaluation, templateinheritance by taking the availability of XPath into account and more.Mako on the other 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.
Micro with Dependencies
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.
Also see the Becoming Big section of the documentation for someinspiration for larger applications based on Flask.
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.
The idea of Flask is to build a good foundation for all applications.Everything else is up to you or extensions.