Flask Subdomains



This content originally appeared on DEV Community and was authored by Brandon Arreguin

In this tutorial, I’m going to show you how to work with subdomains in your Flask application.

But first …

What is a subdomain?

A subdomain is a simple extension of the main domain; it’s almost like a username, but at the start of this. For example, the most commonly used on the web is www, where the main website of a product is frequently hosted. Another example of a subdomain is music.apples.com, where music. is a subdomain of apple.com

Subdomains in Flask

Flask can work easily with subdomains, and a third-party extension isn’t needed for this. Yay!.

You can do it using Nginx, but Nginx is out of the range of this tutorial.

Preparing the environment

First, we need to create a new Python environment to start working with Flask, and update our hosts local file.

  • Create a new folder: mkdir flask-subdomains
  • Enter the folder and start a Python virtualenv inside of this. python -m venv venv
  • Activate your virtualenv.
  • Make a new file named main.py. Also, the templates folder.
  • Open your hosts file and add the domain as follows.
127.0.0.1 superdomain.com
127.0.0.1 admin.superdomain.com
127.0.0.1 user1.superdomain.com

If you don’t know where the hosts file is located. For Linux, go to etc/hosts, and for Windows, run Notepad as administrator, follow the next path c:\Windows\System32\drivers\etc, here is the hosts file located on Windows, then change the search filter from Text Files to All Files, and now you can open your hosts file and edit it.

A minimal Flask application

Now, in our main.py file, let’s create a Flask instance and add some routes.

from flask import Flask, render_template


app = Flask(__name__)
app.config['SERVER_NAME'] = 'superdomain.com:5000'


@app.route('/')
def index():
    context = { 'page_title': 'Home' }
    return render_template('site/index.html.jinja', **context)

Now we have our environment and our minimal Flask application ready to work with.

Our first subdomain

In our main.py file, we need to add a new route and set the subdomain parameter in the decorator

@app.route('/', subdomain='admin')
def admin():
    context = { 'page_title': 'Admin Page' }
    return render_template('admin/index.html', **context)

The subdomain parameter must match the subdomains defined in the hosts file. Let’s run the server.

flask -A main:app --debug run

Open the browser, and run superdomain.com:5000; now, we can access the main page. If we visit admin.superdomain.com:5000, we can see the admin page… Wait, there’s a problem.

Main route in the browser

Admin route doesn't

The admin route is rendering the site template. What’s wrong?

Fixing the problem

In Flask, we need to set one more thing: The subdomain_matching parameter.

from flask import Flask, render_template


app = Flask(__name__, subdomain_matching=True)

Stop the server, and run it again. This time, we were able to access the subdomain index route.

And… Yay!!! Now, our routes are working well.

Admin route working

Working with Blueprints

Flask has the concept of Blueprints, which lets us make our application modular when we need more flexibility and a better structure for larger projects. The Blueprint object can also set a subdomain without passing it in each new route. This is great if you are building a big application.

Let’s modify our current application.

First, let’s create an app folder. The app folder will contain all the new code, and the main.py file will serve as our entrypoint.

Inside the app folder add the __init__.py file and the blueprints folder with the following files: __init__.py, admin.py, site.py, and tenant.py.
It should look like this:

app
  |-blueprints
    |-admin.py
    |-site.py
    |-tenant.py
    |-__init__.py
  |-__init__.py
main.py

Now, we need to define the blueprints for each module, and set the subdomain parameter, as follows…

# blueprints/admin.py
mod = Blueprint('admin', __name__, subdomain='admin')


@mod.route('/')
def index():
    return render_template('admin/index.html.jinja')


# blueprints/site.py
mod = Blueprint('site', __name__)


@mod.route('/')
def index:
    return render_template('site/index.html.jinja')


# blueprints/tenant.py
mod = Blueprint('tenant', __name__, subdomain='<string:subdomain>')


@mod.route('/')
def index(subdomain: str):
    template_name = 'tenant/index.html.jinja'
    return render_template(template_name, subdomain=subdomain)

We set the tenant subdomain to be dynamic and passed it to the index decorated function.
Then, we have to register each blueprint in our main.py file

from flask import Flask

from app.blueprints import admin, site, tenant


app = Flask(__name__, subdomain_matching=True)
app.config['SERVER_NAME'] = 'superdomain.com:5000'
app.register_blueprint(admin.mod)
app.register_blueprint(site.mod)
app.register_blueprint(tenant.mod)

If we visit the user1.superdomain.com:5000, we can see the dynamic tenant route working.

Dynamic subdomain working

Dynamic subdomains could be helpful in some cases, for example, vercel, or the postcard.page site, which allows users to host a site using their domain.
Also, the subdomain allows you to retrieve information from the database related to an organization or user, something like this:

@mod.route('/')
def index(subdomain: str):
    tenant = tenants.get_by_subdomain_or_404(subdomain)
    all_post = posts.get_all_by_tenant(tenant_id=tenant.id)

    return render_template('tenant/index.html.jinja', **{
       'tenant': tenant,
       'posts': all_posts
    })

Conclusion

In this simple tutorial, you learned how to work with subdomains in your Flask application. Remember, this is just at the app level; you’ll need to configure the subdomains in your hosting or domain name providers and use something like Nginx alongside Flask.

I hope you enjoy this post, and thanks for reading. See you next time.

#flask
#webdevelopment
#python


This content originally appeared on DEV Community and was authored by Brandon Arreguin