Issue
I have an app that requires multiple domains pointing to the same app with different data being displayed, but I also have the same "admin" subdomain across all domains which also displays different data depending on the domain.
An example would be:
pinetree.com - displays information about pine trees
oaktree.com - displays information about oak trees
admin.pinetree.com - displays admin for managing pine trees
admin.oaktree.com - displays admin for managing oak trees
So far, I've found that you need to write the SERVER_NAME
(domain name) in the Flask config in order to use subdomains with Flask, but since I have many different types of trees with unique domains, and new trees are added all the time, I don't see how I could use that functionality.
Also, I have seen that GAE flexible doesn't have multitenancy, which is what I had first thought would be the way to manage multiple domains on GAE.
Solution
Subdomain matching, explained in another answer, should be used if you have one base domain with several subdomains. It's more straightforward since Flask can infer more about the URLs it's matching.
However, if you have multiple base domains, you should use host matching instead. You must set host_matching=True
on the app object, as well as setting static_host
so the static
route knows which host to to serve from. Unlike subdomains, you do not set SERVER_NAME
. Then pass the host
option to routes. This matches against the full domain, and so it requires writing out the full domain each time, rather than just the subdomain.
Unfortunately, matching the full host means matching the port as well. Under the dev server, the port will be 5000 by default, but in production the port may be 80, 443, or something else. You can write a small helper to set the port to 5000 when running in development mode (or whatever configuration logic you need for your deployment).
from flask.helpers import get_env
def p(host):
if get_env() == "development":
return host + ":5000"
return host
# p("example.com") -> "example.com:5000"
This example shows routing to any host of the form {tree}tree.com
and admin.{tree}tree.com
, with pinetree.com
as the static host.
from flask import Flask
app = Flask(__name__, host_matching=True, static_host=p("pinetree.com"))
@app.route("/", host=p("<tree>tree.com"))
def index(tree):
return f"{tree} tree"
Blueprint
does not accept a host
option yet, so you'll need to specify the host for each route. You can simplify this a bit using partial
.
from functools import partial
from flask import Blueprint
admin = Blueprint("admin", __name__)
admin_route = partial(admin.route, host=p("admin.<tree>tree.com"))
@admin_route("/")
def index(tree):
return f"admin for {tree} tree"
app.register_blueprint(admin)
Note that the host
can take URL parameters just like the path in the route. It will be passed to views just like path parameters. This allows for dynamic hosts and subdomains. You can use @app.url_defaults
and @app.url_value_preprocessor
to extract this into g
instead of writing it as an argument for each view.
from flask import g
@app.url_value_preprocessor
def extract_tree(endpoint, values):
g.tree = values.pop("tree")
@app.url_defaults
def inject_tree(endpoint, values):
values.setdefault("tree", g.tree)
@app.route("/")
def index()
return f"{g.tree} tree"
During development, add the hosts your hosts file (/etc/hosts
on Unix so they route to localhost.
127.0.0.1 localhost pinetree.com admin.pinetree.com oaktree.com admin.oaktree.com
And run with:
export FLASK_DEBUG=1
flask run
Answered By - davidism
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.