- Managing Flask Session Variables
- Using Flask-Login to Handle User Accounts
- Connect Flask to a Database with Flask-SQLAlchemy
- Demystifying Flask’s Application Factory
- Configuring Your Flask App
- The Art of Routing in Flask
- Organizing Flask Apps with Blueprints
- Handling Forms in Flask with Flask-WTF
- Serving Frontend Assets in Flask
- Powerful Page Templates in Flask With Jinja
- Creating Your First Flask Application
Becoming minimally familiar with Flask is often an exhilarating experience. To Discover Flask is to rediscover instant gratification itself: all it takes is a mere few seconds for anybody deploy a Python application, alive and well on a development server. I believe the cliche that the hardest part of anything is getting started, so it's no surprise that Flaks can get the imagination brewing.
Indeed, there's no monolithic
settings.py files in Flask (thank god). In fact, if you're to take Flask's documentation seriously, they might have you think that configuring Flask can happen any where in your code, any time you want:
The above works just fine. As the Flask docs promise, your app's config can be modified as you would a dictionary, and can be modified basically anywhere. But hold on a minute, young Padawan. With great power comes great responsibility. Do you really want your Flask app to start looking like this?:
The point is, config variables belong in config files for a reason... especially sensitive variables. Today we'll be looking at some options and best practices for how to configure your Flask app, and which config variables you should know about.
Types of Config Files
Flask does us the courtesy of providing other configuration options to its, ehm, wonderful inline approach. These options includes configuration via object, envvar, or pyfile. We set the type of configuration file/type we want to use upfront when creating the app, like any of the below three demonstrations:
... app.config.from_object('config.Config') # Config from object app.config.from_envvar('APP_CONFIG') # Config from filepath in env app.config.from_pyfile('application.cfg', silent=True) # Config from cfg file
Configure from Object
Configuring from an "object" is actually referring to creating a Python class, in which we can store all our variables in. This is our bread and butter, being the cleanest and most extensible way of handling Flask configurations. Let's see what a barebones
Config class looks like. Something like the following is usually found in a
config.py file in the root of your app:
Simple and easy to understand! Each variable in our Config class is a reserved Flask config variable name. All we need to do is set these values, and Flask will know what we're talking about.
This is an improvement, but there's still a lot wrong... namely, we haven't solved the problem of our secret key hiding in plain sight! This is only the beginning of what kind of shitstorm would unfold if this file fell into the wrong hands, or is commited to version control. Here's a more realistic assortment of what a real app might hold in its config:
I think you get the message. So what do we do? Add config.py to .gitignore and call it a day, right? WRONG.
The most obvious problem with omitting your config file from source control is that this will almost certainly go wrong at some point. Think you'll never forget to check your .gitignore file? Fine, but what about when that loser who sits across from you clones your repo and asks for that file? Will he know to omit it? Will YOU know to omit it when he changes the filename to config_v2_FINAL.py?
Security aside, there's another reason why hiding config.py is a bad idea. When the next person contributes to our codebase, how could they possibly know which variables live in your lone copy of the file? No matter how clan your code is, nobody will fully understand what's happening if they can't see what types of variables are driving the entire app. Here's my personal favorite solution this:
Oh snap, environment variables! By storing our values in a .env file, we can reap the benefits 0f BOTH a config file and and .env file. The corresponding .env would look like this:
TESTING=True DEBUG=True SECRET_KEY=_5#y2L"F4Q8z\n\xec]/ SESSION_COOKIE_NAME=my_cookie
The added bonus here is now we can have multiple config classes, depending on whether we're in prod or dev:
That's more like it!
Configure from Envvar
As mentioned previously, this is set via
app.config.from_envvar('APP_CONFIG'). This option is absurdly simple: all we're saying is "I have a config file somewhere, which you can find in my .env file." We're still pointing to a file all the same, we've just taken another step in how its defined.
Configure from PyFile
Finally, there's this guy:
app.config.from_pyfile('application.cfg',silent=True). I actually haven't ever configured a Flask app in this way, and truthfully don't care enough to go into detail about it :).
Enough all these files... what do Flask config vars actually mean? Once we configure common plugins alongside our default Flask app, things can get a bit nuts. Consider this a crash course, but is by no means required reading:
I'll spare you the obscure variables, but here are the basics:
- FLASK_ENV: The environment the app is running in, such as development or production. Setting the environment to development mode will automatically trigger other variables, such as setting DEBUG to True. Flask plugins similarly behave differently when this is true.
- DEBUG: Extremely useful when developing! DEBUG mode triggers a number of things. Exceptions thrown by the app will print to console automatically, app crashes will result in a useful error screen, and your app will auto reload when changes are detected.
- TESTING: Useful when running automated tests, this mode ensures exceptions are propagated rather than handled by the the app’s error handlers.
- SECRET_KEY: A string which is used to encrypt sensitive information, such as passwords. If somebody has a hold of your app's secret key, the security of your app is compromised. Guard this collection of random characters with your life.
- SERVER_NAME: If your app is to be served at a custom domain, this is specified here.
Static Asset Configuration
Configuration for serving static assets via the Flask-Assets library:
- ASSETS_DEBUG: Enables/disables a debugger specifically for static assets.
- COMPRESSOR_DEBUG: Enables/disables a debugger for asset compressors, such as LESS or SASS
- FLASK_ASSETS_USE_S3: Boolean value specifying whether or not assets should be served from S3
- FLASK_ASSETS_USE_CDN:Boolean value specifying whether or not assets should be served from a CDN
Flask-SQLAlchemy is pretty much the choice for handling database connections in Flask:
- SQLALCHEMY_DATABASE_URI: The connection string of your app's database.
- SQLALCHEMY_ECHO: Prints database-related actions to console for debugging purposes.
- SQLALCHEMY_ENGINE_OPTIONS: Additional options to be passed to the SQLAlchemy engine which holds your app's database connection.
Flask-Session is excellent for apps which store user information per session. While Flask supports storing information in cookies by default, Flask-Session builds on this by adding functionality and additional methods for where to store session information:
- SESSION_TYPE: Session information can be handled via
- SESSION_PERMANENT: A True/False value which states whether or not user sessions should last forever.
- SESSION_KEY_PREFIX: Modifies the key names in session key/value pairs to always have a certain prefix.
- SESSION_REDIS: URI of a Redis instance to store session information.
- SESSION_MEMCACHED: URI of a memcached client to store session information.
- SESSION_MONGODB: URI of a MongoDB database to store session information.
- SESSION_SQLALCHEMY: URI of a database to store session information.
We've configured a shit ton so far, it seems. And yet, there are still bits and pieces flying around Flask which inevitably need to be configured inline. Consider
app = Flask(__name__, instance_relative_config=False): indeed, we've set a configuration value when creating our app! But wasn't the point to avoid this?! Well, yes, but check out what we configured: the location of
I think we've all had enough configuring for one day. Hopefully this cleared something up for somebody out there!