I'll start by answering your questions and then I'll continue with a few explanations regarding configuration best practices.
So I am wondering if it is safe to use multiple methods from above?
Yes. And it's in fact recommended that you use multiple ways of loading your configuration (see below why).
So will the settings from step 3 overwrite all previous (previously
set) settings correctly? And is this good practice?
Yes. And usually the best way to learn things is to try things out. So I'll give you an example below, using classes and inheritance just to prove how overriding works (you can paste this in a Python module and run it, assuming you have Flask installed), but you should go ahead and experiment with all of the above methods that you've read about and mix and match them to solidify your knowledge.
from flask import Flask
class Config:
LOGGING_CONFIGURATION = None
DATABASE = None
class DevelopmentConfig(Config):
LOGGING_CONFIGURATION = 'Development'
DATABASE = 'development.db'
class TestConfig(Config):
LOGGING_CONFIGURATION = 'Test'
DATABASE = 'test.db'
def create_app(config):
app = Flask(__name__)
app.config.from_object(Config) # we load the default configuration
print(app.config['LOGGING_CONFIGURATION'], app.config['DATABASE'])
app.config.from_object(config) # we override with the parameter config
print(app.config['LOGGING_CONFIGURATION'], app.config['DATABASE'])
return app
if __name__ == '__main__':
app = create_app(DevelopmentConfig)
test_app = create_app(TestConfig) # you do this during tests
outputs
None None
Development development.db
None None
Test test.db
And Doesn't it negate the benefit of using factory pattern since I
have read that not using factory pattern (for example while unit
testing) could result in certain config if updated will not apply
correctly. Hence create factory pattern to get fresh object with the
needed config applied.
No. You're confusing things here, loading configurations and using application factories are NOT mutually exclusive. In fact, they work together.
You are correct that messing with some config values, such as ENV and DEBUG, which are special config values may cause the application to behave inconsistently. More details on those special vales here. So try not to alter those once the application has finished setting up. See more in the word of advice section below.
Further clarifications
The way Flask is designed usually requires the configuration to be available when the application starts up, and you usually need SOME KIND of configuration because, depending on your environment you might want to change different settings like:
- the SECRET KEY for example (if you're using Cookies for instance)
- toggle the DEBUG mode (you definitely don't want to use this in production, but you do in development mode)
- using a different DATABASE (which might be a path to a different file, if you're using SQLITE3, which is super useful in tests, where you don't want to use the production database)
- using a different logging configuration (you'll maybe want critical information logged to a FILE in production, but in development you'll want everything logged to the CONSOLE, for debugging purposes)
- etc
Now you see that, because you need different settings for different configurations, you'll need some kind of mechanism to toggle between configurations, which is why using all those methods above combined is useful and recommended, because generally you'll load some kind of DEFAULT configuration, and override accordingly based on the environment (PRODUCTION, DEVELOPMENT, etc).
The way you would achieve all the above is described very meticulously with enough examples here, which is beyond the scope of this thread, but I'd encourage you to go through each example and try it out.
I also have an open source app, built with Flask, where I've used classes and inheritance to load different configurations based on different environments and went even further to customise those as well. So not only do I load, say, the DEVELOPMENT configuration, but I even customise it further should I want to. Examples are here, and I load them from here. Do try to understand the mechanism by which I load the configuration, it will help you.
Edit:
The config object is just a subclass of a dictionary that has additional methods that help you load configurations from different places (the ones you've seen above):
- It doesn't matter how you load or override your configuration. All the settings will be loaded into the
config
dictionary. This belongs to the Flask object. This is where your configuration will live.
- So it doesn't matter whether you do:
# override a setting if it exists or add it if it doesn't
# just like you would do in a normal dictionary
app.config[key] = value
# this will either override all the values that are loaded
# from that object or it will add them
app.config.from_pyobject(object)
# same as above, and the same goes for any method you use to add/override
app.config.from_pyfile(filename)
The same goes for any other method not listed here that might load/override settings. They all have the same precedence, just be careful of the order in which you override, the configuration you load last is the one that the app will use.
Word of advice
Try to load your configuration, along with all its core settings, regardless of your environment, as soon as your application starts up, and don't alter them from that point forward. If you find you are altering a lot of settings from different parts of the application, after the application is running (i.e you're doing a lot of app[value] = another_value
), try to rethink your design, as that is an unstable implementation. What if a part of an application is expecting a setting that hasn't been set up by another part of the application ? Don't do that.