Flask is a handy python micro-framework for developing web applications and APIs. It is definitely my tool of choice when developing smaller web applications. On occasion, I even use it for larger projects due to the freedom and flexibility it allows in terms of packages/plug-ins. In this article, I will demonstrate how to organise flask configurations.
A lot of times, apart from a development environment, the apps that we work on will have to be deployed to a production environment. A place where they are universally accessible and open to the general public. This environment will need its own configurations such as databases, secret keys, email servers, etc. In some cases, your project may even have more environments such as testing and staging. The number of environments you have is irrelevant (if you do things the right way), as you will need a different set of configurations for each environment. I hope that you do not wish to run tests on your production database!
The beginner’s method
To start off, let’s create a simple flask app with one page and set the configurations:
from flask import Flask app = Flask(__name__) app.config['APP_ROOT'] = os.path.dirname(os.path.abspath(__file__)) app.config['MONGO_URI'] = "mongo_uri" app.config['SECRET_KEY'] = "secret_key" @app.route("/") def index(): return "Welcome Home!", 200 if __name__ == "__main__": app.run()
We’ll keep the views simple as our focus on this article is the app configurations. In this example, the app has one page that returns the text “Welcome Home!” to the user’s browser along with an OK status code. However, pay attention to the 2 lines of code after initialising the app object.
These are the app configurations. These are variables we set in order to use throughout our application. in order to access these values, simply reference them by using app.config[key]. This example is simple enough, and it might be sufficient for a simple application that runs on a single environment. However, it is not the most sustainable or scalable method of handling Flask configurations.
Let’s start with the pros of this method:
- This method is a very quick way of configuring our applications and while keeping all the code in one file.
- Keeps our application simple if we plan on running it in only one environment (which is extremely rare for non-experimental projects as we would need at least development & production environments).
That’s about it. This is the method that’s taught in most beginner Flask tutorials and I personally don’t see much use for it after that.
- If you have many app configuration values, the file can quickly become too long.
- If you have multiple environments, you will have to manually adjust the app configurations for each environment with every release. This can become a nightmare very quickly.
- In addition to the former point, you’re more prone to make mistakes while updating app configuration values between environments
- If there are any configurations that you want to keep secret (from a public repository for example), you will have a difficult time doing that with this method (although you could use environment variables, that’s a topic I’ve covered in this article).
As you can see, the cons far outweigh the pros, and there are many more cons we can specify, but I’ll leave it at that because I’m sure the point has been proven. That being said, this method is not sustainable for large projects or projects that are expected to run on multiple environments.
So what are some ways in which we can better organise our application’s configurations in order to avoid the pitfalls listed above? Well, that’s what this article is for! I’m going to describe 2 of my favourite methods of organising configurations.
1. Configuration files
The first method is to utilise configuration files. In this method, we will have multiple configuration files, one for each environment. These will contain the configurations for that specific environment.
The file should be a .cfg file and its content will be as follows:
MONGO_URI = "environment_mongo_uri" SECRET_KEY = "environment_secret_key"
Once you’ve created all the relevant files for the relevant environments, you can import them into your app by replacing the app.config lines with:
Note that in this example, the files are loaded in the order specified so if there is a variable defined in both production.cfg and development.cfg, the development.cfg value will override the one specified in production.cfg.
This means that with this method, we have to remove the line importing the development file before pushing the code to production. This seems simple enough and is a better way of organising the files than the beginner’s method, but as you can probably already tell, this will still get tedious if there are more than 2 environments to worry about. It also requires a separate file for each environment.
A clever way of handling multiple environments is by wrapping the configuration imports in a try/except block and then ignoring the environment-specific files from version control. So, in the example above, we would do the following:
app.config.from_pyfile('production.cfg') try: app.config.from_pyfile('development.cfg') except FileNotFoundError: pass
We then ignore the development.cfg file in version control. This removes the need to manually delete/comment the line that reads the file. In production, the file will not be found, and the application will carry on.
2. Configuration objects
Another way of handling configurations is by using classes. Here, we create a base class defining common configurations and then create classes that inherit from this base class for each of our environments. Ideally, we’d do this in a separate module. The content of the file could be as follows:
class BaseConfig(object): DEBUG = False MONGO_URI = "mongo_uri" class DevelopmentConfig(BaseConfig): DEBUG = True MONGO_URI = "development_mongo_uri" class TestingConfig(BaseConfig): DEBUG = True MONGO_URI = "testing_mongo_uri" class ProductionConfig(BaseConfig): MONGO_URI = "production_mongo_uri"
As you can see in the example above, we can create as many classes as we need depending on the number of environments we have. In order to use the configurations above, we simply use the following line of code:
The above line will load the production config. Replace ProductionConfig with the relevant class for a particular environment. You can also create an instance of the object and pass it to app.config.from_object()
These are a couple of ways to handle configurations in Flask that make handling multiple environments a lot less tedious. These are not the ONLY methods though, there is another very handy method: Using environment variables. Be sure to give these a try in your next project, or even your current one. It’s never too late to refactor!
You can check out Flask’s official documentation regarding handling configuration here.
Enjoyed the article or found it useful? Help out with a share and spread the word to other developers! Also feel free to drop a comment below. I’d love to hear your thoughts