Flask configuration can be a surprisingly tricky beast to get right, especially when you need to manage vastly different settings for development and production environments.
Let’s see this in action. Imagine a simple Flask app:
from flask import Flask
import os
app = Flask(__name__)
# Load configuration from environment variables or a config file
config_name = os.environ.get('APP_CONFIG', 'development')
app.config.from_object(f'config.{config_name.capitalize()}')
@app.route('/')
def hello():
return f"Hello! The database URI is: {app.config.get('SQLALCHEMY_DATABASE_URI')}"
if __name__ == '__main__':
app.run(debug=app.config.get('DEBUG'))
Now, let’s define our configuration files. Create a config.py file in the same directory:
# config.py
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY', 'a-very-secret-key-for-dev')
DEBUG = False
TESTING = False
SQLALCHEMY_DATABASE_URI = 'sqlite:///default.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False
class Development(Config):
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'
class Production(Config):
SECRET_KEY = os.environ.get('SECRET_KEY') # Must be set in production
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') # Must be set in production
To run this in development:
# In your terminal, in the same directory as app.py and config.py
export APP_CONFIG='development'
python app.py
You’ll see: Hello! The database URI is: sqlite:///dev.db and the app will run with debug=True.
To run this in production (hypothetically, as we’re not actually deploying):
# In your terminal, in the same directory as app.py and config.py
export APP_CONFIG='production'
export SECRET_KEY='your_super_secret_production_key'
export DATABASE_URL='postgresql://user:password@host:port/dbname' # Example
python app.py
You’ll see: Hello! The database URI is: postgresql://user:password@host:port/dbname and the app will run with debug=False.
This system solves the problem of having a single codebase that needs to behave differently based on its deployment environment. It leverages Python’s object-oriented features and Flask’s built-in configuration loading to keep sensitive information out of your code and to tailor settings like database connections, secret keys, and debugging modes. The core idea is to define a base configuration and then inherit from it for specific environments, overriding only what’s necessary.
The app.config.from_object() method is key here. It takes a string that represents a Python dotted path to an object (like a class or module). Flask then inspects that object and loads its attributes into the application’s configuration dictionary. This allows for a clean separation of concerns, where your config.py file becomes the single source of truth for all environment-specific settings.
The os.environ.get() calls are crucial for production. They allow you to inject configuration values from the operating system’s environment variables. This is a standard practice for security and flexibility, as you can change these values without redeploying your application. For example, your DATABASE_URL or SECRET_KEY can be set directly in your server’s environment or managed by a deployment platform like Heroku or AWS.
What most people don’t realize is how much power from_object grants you. You’re not limited to just classes. You could have a config.py with individual configuration variables directly at the module level, and from_object('config') would load them. Or, if you had a more complex setup, you could even have from_object('my_package.configs.production_settings') pointing to a specific module deep within your project. The flexibility is immense.
The next step is often integrating this configuration with external configuration files like .env files using libraries like python-dotenv for even easier local development setup.