This introductory chapter will help you to understand the different ways Flask can be configured to suit various needs as per the demands of the project.
In this chapter, we will cover the following recipes:
Environment setup with virtualenv
Handling basic configurations
Class-based settings
Organization of static files
Being deployment specific with instance folders
Composition of views and models
Creating a modular web app with blueprints
Making a Flask app installable using setuptools
"Flask is a microframework for Python based on Werkzeug, Jinja2 and good intentions." | ||
--Flask official documentation |
Why micro? Does it mean that Flask is lacking in functionality or that your complete web application has to mandatorily go inside one file? Not really! It simply refers to the fact that Flask aims at keeping the core of the framework small but highly extensible. This makes writing applications or extensions very easy and flexible and gives developers the power to choose the configurations they want for their application, without imposing any restrictions on the choice of database, templating engine, and so on. In this chapter, you will learn some ways to set up and configure Flask.
Getting started with Flask hardly takes 2 minutes. Setting up a simple Hello World application is as easy as baking a pie:
from flask import Flask app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello to the World of Flask!' if __name__ == '__main__': app.run()
Now, Flask needs to be installed; this can be done simply via pip
:
$ pip install Flask
The preceding snippet is a complete Flask-based web application. Here, an instance of the imported Flask
class is a Web Server Gateway Interface (WSGI) (http://legacy.python.org/dev/peps/pep-0333/) application. So, app
in this code becomes our WSGI application, and as this is a standalone module, we set the __name__
string as '__main__'
. If we save this in a file with the name app.py
, then the application can simply be run using the following command:
$ python app.py * Running on http://127.0.0.1:5000/
Now, if we just head over to our browser and type http://127.0.0.1:5000/
, we can see our application running.
Flask can be installed using pip
or easy_install
globally, but we should always prefer to set up our application environment using virtualenv
. This prevents the global Python installation from getting affected by our custom installation by creating a separate environment for our application. This separate environment is helpful because you can have multiple versions of the same library being used for multiple applications, or some packages might have different versions of the same libraries as dependencies. virtualenv
manages this in separate environments and does not let a wrong version of any library affect any application.
We will first install virtualenv
using pip
and then create a new environment with the name my_flask_env
inside the folder in which we ran the first command. This will create a new folder with the same name:
$ pip install virtualenv $ virtualenv my_flask_env
Now, from inside the my_flask_env
folder, we will run the following commands:
$ cd my_flask_env $ source bin/activate $ pip install flask
This will activate our environment and install Flask inside it. Now, we can do anything with our application within this environment, without affecting any other Python environment.
Until now, we have used pip install flask
multiple times. As the name suggests, the command refers to the installation of Flask just like any Python package. If we look a bit deeper into the process of installing Flask via pip
, we will see that a number of packages are installed. The following is a summary of the package installation process of Flask:
$ pip install -U flask Downloading/unpacking flask …........ …........ Many more lines......... …........ Successfully installed flask Werkzeug Jinja2 itsdangerous markupsafe Cleaning up...
Note
In the preceding command, -U
refers to the installation with upgrades. This will overwrite the existing installation (if any) with the latest released versions.
If we notice carefully, there are five packages installed in total, namely flask
, Werkzeug
, Jinja2
, itsdangerous
, and markupsafe
. These are the packages on which Flask depends, and it will not work if any of them are missing.
To make our lives easier, we can use virtualenvwrapper
, which, as the name suggests, is a wrapper written over virtualenv
and makes the handling of multiple virtualenv
easier.
Tip
Remember that the installation of virtualenvwrapper
should be done at a global level. So, deactivate any virtualenv
that might still be active. To deactivate it, just use the following command:
$ deactivate
Also, it is possible that you might not be able to install the package at a global level because of permission issues. Switch to superuser or use sudo
in this case.
You can install virtualenvwrapper
using the following commands:
$ pip install virtualenvwrapper $ export WORKON_HOME=~/workspace $ source /usr/local/bin/virtualenvwrapper.sh
In the preceding code, we installed virtualenvwrapper
, created a new environment variable with the name WORKON_HOME
, and provided it with a path, which will act as the home for all our virtual environments created using virtualenvwrapper
. To install Flask, use the following commands:
$ mkvirtualenv flask $ pip install flask
To deactivate a virtualenv
, we can just run the following command:
$ deactivate
To activate an existing virtualenv
using virtualenvwrapper
, we can run the following command:
$ workon flask
References and installation links are as follows:
The first thing that comes to mind is configuring a Flask application as per the need. In this recipe, we will try to understand the different ways in which Flask configurations can be done.
In Flask, a configuration is done on an attribute named config
of the Flask
object. The config
attribute is a subclass of the dictionary data type, and we can modify it just like any dictionary.
For instance, to run our application in the debug mode, we can write the following:
app = Flask(__name__) app.config['DEBUG'] = True
Tip
The debug
Boolean can also be set at the Flask
object level rather than at the config
level:
app.debug = True
Alternatively, we can use this line of code:
app.run(debug=True)
Enabling the debug mode will make the server reload itself in the case of any code changes, and it also provides the very helpful Werkzeug
debugger when something goes wrong.
There are a bunch of configuration values provided by Flask. We will come across them in the relevant recipes.
As the application grows larger, there originates a need to manage the application's configuration in a separate file as shown here. Being specific to machine-based setups in most cases will most probably not be a part of the version-control system. For this, Flask provides us with multiple ways to fetch configurations. The most frequently used ones are discussed here:
From a Python configuration file (
*.cfg
), the configuration can be fetched using:app.config.from_pyfile('myconfig.cfg')
From an object, the configuration can be fetched using:
app.config.from_object('myapplication.default_settings')
Alternatively, we can also use:
app.config.from_object(__name__) #To load from same file
From the environment variable, the configuration can be fetched using:
app.config.from_envvar('PATH_TO_CONFIG_FILE')
Flask is intelligent enough to pick up only those configuration variables that are written in uppercase. This allows us to define any local variables in our configuration files/objects and leave the rest to Flask.
Tip
The best practice to use configurations is to have a bunch of default settings in app.py
or via any object in our application itself and then override the same by loading it from the configuration file. So, the code will look like this:
app = Flask(__name__) DEBUG = True TESTING = True app.config.from_object(__name__) app.config.from_pyfile('/path/to/config/file')
An interesting way of laying out configurations for different deployment modes, such as production, testing, staging, and so on, can be cleanly done using the inheritance pattern of classes. As the project gets bigger, you can have different deployment modes such as development, staging, production, and so on, where each mode can have several different configuration settings, and some settings will remain the same.
We can have a default setting base class, and other classes can inherit this base class and override or add deployment-specific configuration variables.
The following is an example of our default setting base class:
class BaseConfig(object): 'Base config class' SECRET_KEY = 'A random secret key' DEBUG = True TESTING = False NEW_CONFIG_VARIABLE = 'my value' class ProductionConfig(BaseConfig): 'Production specific config' DEBUG = False SECRET_KEY = open('/path/to/secret/file').read() class StagingConfig(BaseConfig): 'Staging specific config' DEBUG = True class DevelopmentConfig(BaseConfig): 'Development environment specific config' DEBUG = True TESTING = True SECRET_KEY = 'Another random secret key'
Now, we can use any of the preceding classes while loading the application's configuration via from_object()
. Let's say that we save the preceding class-based configuration in a file named configuration.py
:
app.config.from_object('configuration.DevelopmentConfig')
So, overall, this makes the management of configurations for different deployment environments flexible and easier.
Tip
Downloading the example code
You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.
Organizing static files such as JavaScript, stylesheets, images, and so on efficiently is always a matter of concern for all web frameworks.
Flask recommends a specific way to organize static files in our application:
my_app/ - app.py - config.py - __init__.py - static/ - css/ - js/ - images/ - logo.png
While rendering them in templates (say, the logo.png
file), we can refer to the static files using the following line of code:
<img src='/static/images/logo.png'>
If there exists a folder named static
at the application's root level, that is, at the same level as app.py
, then Flask will automatically read the contents of the folder without any extra configuration.
Alternatively, we can provide a parameter named static_folder
to the application object while defining the application in app.py
:
app = Flask(__name__, static_folder='/path/to/static/folder')
In the
img src
path in the How to do it… section, static
refers to the value of static_url_path
on the application object. This can be modified as follows:
app = Flask( __name__, static_url_path='/differentstatic', static_folder='/path/to/static/folder' )
Now, to render the static file, we will use the following:
<img src='/differentstatic/logo.png'>
Flask provides yet another way of configuration where we can efficiently manage deployment-specific parts. Instance folders allow us to segregate deployment-specific files from our version-controlled application. We know that configuration files can be separate for different deployment environments such as development and production, but there are many more files such as database files, session files, cache files, and other runtime files. So, we can say that an instance folder is like a holder bin for these kinds of files.
By default, the instance folder is picked up from the application automatically if we have a folder named instance
in our application at the application level:
my_app/ - app.py - instance/ - config.cfg
We can also explicitly define the absolute path of the instance folder using the instance_path
parameter on our application object:
app = Flask( __name__, instance_path='/absolute/path/to/instance/folder' )
To load the configuration file from the instance folder, we will use the instance_relative_config
parameter on the application object:
app = Flask(__name__, instance_relative_config=True)
This tells the application to load the configuration file from the instance folder. The following example shows how this will work:
app = Flask( __name__, instance_path='path/to/instance/folder', instance_relative_config=True ) app.config.from_pyfile('config.cfg', silent=True)
In the preceding code, first, the instance folder is loaded from the given path, and then, the configuration file is loaded from the file named config.cfg
in the given instance folder. Here, silent=True
is optional and used to suppress the error in case config.cfg
is not found in the instance folder. If silent=True
is not given and the file is not found, then the application will fail, giving the following error:
IOError: [Errno 2] Unable to load configuration file (No such file or directory): '/absolute/path/to/config/file'
Note
It might seem that loading the configuration from the instance folder using instance_relative_config
is redundant work and can be moved to one of the configuration methods. However, the beauty of this process lies in the fact that the instance folder concept is completely independent of configuration, and instance_relative_config
just compliments the configuration object.
As we go big, we might want to structure our application in a modular manner. We will do this by restructuring our Hello World application.
First, create a new folder in our application and move all our files inside this new folder.
Then, create
__init__.py
in our folders, which are to be used as modules.After that, create a new file called
run.py
in the topmost folder. As the name implies, this file will be used to run the application.Finally, create separate folders to act as modules.
Refer to the following file structure for a better understanding:
flask_app/ - run.py - my_app/ – __init__.py - hello/ - __init__.py - models.py - views.py
First, the flask_app/run.py
file will look something like the following lines of code:
from my_app import app app.run(debug=True)
Then, the flask_app/my_app/__init__.py
file will look something like the following lines of code:
from flask import Flask app = Flask(__name__) import my_app.hello.views
Then, we will have an empty file just to make the enclosing folder a Python package, flask_app/my_app/hello/__init__.py
:
# No content. # We need this file just to make this folder a python module.
The models file, flask_app/my_app/hello/models.py
, has a non-persistent key-value store:
MESSAGES = { 'default': 'Hello to the World of Flask!', }
Finally, the following is the views file, flask_app/my_app/hello/views.py
. Here, we fetch the message corresponding to the key that is asked for and also have a provision to create or update a message:
from my_app import app from my_app.hello.models import MESSAGES @app.route('/') @app.route('/hello') def hello_world(): return MESSAGES['default'] @app.route('/show/<key>') def get_message(key): return MESSAGES.get(key) or "%s not found!" % key @app.route('/add/<key>/<message>') def add_or_update_message(key, message): MESSAGES[key] = message return "%s Added/Updated" % key
We can see that we have a circular import between my_app/__init__.py
and my_app/hello/views.py
, where, in the former, we import views
from the latter, and in the latter, we import the app
from the former. So, this actually makes the two modules depend on each other, but here, it is actually fine as we won't be using views
in my_app/__init__.py
. We do the import of views
at the bottom of the file so that they are not used anyway.
We have used a very simple non-persistent in-memory key-value store for the demonstration of the model layout structure. It is true that we could have written the dictionary for the MESSAGES
hash map in views.py
itself, but it's best practice to keep the model and view layers separate.
So, we can run this app using just run.py
:
$ python run.py * Running on http://127.0.0.1:5000/ * Restarting with reloader
Note
The reloader indicates that the application is being run in the debug mode, and the application will reload whenever a change is made in the code.
Now, we can see that we have already defined a default message in MESSAGES
. We can view this message by opening http://127.0.0.1:5000/show/default
. To add a new message, we can type http://127.0.0.1:5000/add/great/Flask%20is%20greatgreat!!
. This will update the MESSAGES
key-value store to look like the following:
MESSAGES = { 'default': 'Hello to the World of Flask!', 'great': 'Flask is great!!', }
Now, if we open the link http://127.0.0.1:5000/show/great
in a browser, we will see our message, which, otherwise, would have appeared as a not-found message.
A blueprint is a concept in Flask that helps make large applications really modular. They keep application dispatching simple by providing a central place to register all the components in the application. A blueprint looks like an application object but is not an application. It looks like a pluggable application or a smaller part of a bigger application, but it is not so. A blueprint is actually a set of operations that can be registered on an application and represents how to construct or build an application.
We will take the application from the previous recipe, Composition of views and models, as a reference and modify it to work using blueprints.
The following is an example of a simple Hello World application using blueprints. It will work in a manner similar to the previous recipe but is much more modular and extensible.
First, we will start with the flask_app/my_app/__init__.py
file:
from flask import Flask from my_app.hello.views import hello app = Flask(__name__) app.register_blueprint(hello)
Next, the views file, my_app/hello/views.py
, will look like the following lines of code:
from flask import Blueprint from my_app.hello.models import MESSAGES hello = Blueprint('hello', __name__) @hello.route('/') @hello.route('/hello') def hello_world(): return MESSAGES['default'] @hello.route('/show/<key>') def get_message(key): return MESSAGES.get(key) or "%s not found!" % key @hello.route('/add/<key>/<message>') def add_or_update_message(key, message): MESSAGES[key] = message return "%s Added/Updated" % key
We have defined a blueprint in the flask_app/my_app/hello/views.py
file. We don't need the application object anymore here, and our complete routing is defined on a blueprint named hello
. Instead of @app.route
, we used @hello.route
. The same blueprint is imported in flask_app/my_app/__init__.py
and registered on the application object.
We can create any number of blueprints in our application and do most of the activities that we would do with our application, such as providing different template paths or different static paths. We can even have different URL prefixes or subdomains for our blueprints.
This application will work in exactly the same way as the last application. The only difference is in the way the code is organized.
So, we have a Flask application now, but how do we install it just like any Python package? It is possible that any other application depends on our application or our application is in fact an extension for Flask and would need to be installed in a Python environment so that it can be used by other applications.
Installing a Flask app can be achieved very easily using the setuptools
library of Python. We will have to create a file called setup.py
in our application's folder and configure it to run a setup script for our application. It will take care of any dependencies, descriptions, loading test packages, and so on.
The following is an example of a simple setup.py
script for our Hello World application:
#!/usr/bin/env python # -*- coding: UTF-8 -*- import os from setuptools import setup setup( name = 'my_app', version='1.0', license='GNU General Public License v3', author='Shalabh Aggarwal', author_email='contact@shalabhaggarwal.com', description='Hello world application for Flask', packages=['my_app'], platforms='any', install_requires=[ 'flask', ], classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], )
In the preceding script, most of the configuration is self-explanatory. The classifiers are used when we make this application available on PyPI. These will help other users search the application using these classifiers.
Now, we can just run this file with the install
keyword as shown here:
$ python setup.py install
This will install this application along with all its dependencies mentioned in install_requires
, that is, Flask and all the dependencies of Flask as well. Then, this app can be used just like any Python package in our Python environment.
The list of valid trove classifiers can be found at https://pypi.python.org/pypi?%3Aaction=list_classifiers