Subdomain-based configuration for a Flask local development server
This example shows how to set up a Flask local development server to use a different configuration based on the subdomain of the request. The project I work on has several environments (dev, qa, staging, etc). Each environment has different database and API hostnames. I use this to switch between database and API environments quickly while using my local development server.
This assumes a create_app
function is used to create the Flask application instance as described in the Application Factories Flask documentation.
create_app¶
Modify the create_app
function to take a configobj
argument and use it to override the default configuration.
def create_app(configobj=None):
app = Flask(__name__)
# Default configuration
app.config.from_object(__name__)
# Override configuration using config passed into create_app
if configobj:
app.config.from_object(configobj)
return app
SubdomainDispatcher¶
The SubdomainDispatcher is taken from the Application Dispatching Flask documentation. It is WSGI middleware that looks at the subdomain of the request and returns a different application instance for each subdomain. It calls the create_app
function above and passes it the appropriate configuration object for the subdomain.
class SubdomainDispatcher(object):
def __init__(self, create_app, domain=''):
"""
:param create_app: a function that returns a `flask.Flask` instance
:param domain: str - used to determine the subdomain
"""
self.create_app = create_app
self.domain = domain
self.lock = Lock()
self.instances = {}
def __call__(self, environ, start_response):
app = self._get_application(environ['HTTP_HOST'])
return app(environ, start_response)
def _get_application(self, host):
host = host.split(':')[0]
assert host.endswith(self.domain), 'Configuration error'
subdomain = host[:-len(self.domain)].rstrip('.')
with self.lock:
app = self.instances.get(subdomain)
if app is None:
configobj = self._get_subdomain_based_config(subdomain)
app = self.create_app(configobj=configobj)
self.instances[subdomain] = app
return app
@staticmethod
def _get_subdomain_based_config(subdomain):
class Config(object):
pass
config = Config()
if subdomain == 'dev':
config.API_HOST = 'dev-host'
config.DB_SERVER = 'dev-db-server'
elif subdomain == 'qa':
config.API_HOST = 'qa-host'
config.DB_SERVER = 'qa-db-server'
return config
rundevserver¶
rundevserver
is similar to flask.Flask.run
but uses the SubdomainDispatcher
middleware before calling werkzeug.serving.run_simple
.
def rundevserver(host=None, port=None, domain='', debug=True, **options):
"""
Modified from `flask.Flask.run`
Runs the application on a local development server.
:param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to
have the server available externally as well. Defaults to
``'127.0.0.1'``.
:param port: the port of the webserver. Defaults to ``5000``
:param domain: used to determine the subdomain
:param debug: if given, enable or disable debug mode.
See :attr:`debug`.
:param options: the options to be forwarded to the underlying
Werkzeug server. See
:func:`werkzeug.serving.run_simple` for more
information.
"""
from werkzeug.serving import run_simple
if host is None:
host = '127.0.0.1'
if port is None:
port = 5000
options.setdefault('use_reloader', debug)
options.setdefault('use_debugger', debug)
app = SubdomainDispatcher(create_app, domain, debug=debug)
run_simple(host, port, app, **options)
Usage¶
- Add the following to your hosts file (/etc/hosts on Ubuntu):
0.0.0.0 dev.localhost 0.0.0.0 qa.localhost
- Run the local dev server:
if __name__ == '__main__': rundevserver(host='0.0.0.0', port=5000, domain='localhost')
- Use the following URLs to get different app configurations:
- http://localhost:5000 (default configuration)
- http://dev.localhost:5000
- http://qa.localhost:5000
Github code¶
A full working example is located on github: flask-subdomain-dispatcher-example An updated version of the code is here: flask-subdomaindevserver.