Explore Flask

7.4. How do you use them?

7.4.1. Basic usage

Let's take a look at the code for one of the blueprints from that Facebook example.

# facebook/views/profile.py

from flask import Blueprint, render_template

profile = Blueprint('profile', __name__)

@profile.route('/<user_url_slug>')
def timeline(user_url_slug):
    # Do some stuff
    return render_template('profile/timeline.html')

@profile.route('/<user_url_slug>/photos')
def photos(user_url_slug):
    # Do some stuff
    return render_template('profile/photos.html')

@profile.route('/<user_url_slug>/about')
def about(user_url_slug):
    # Do some stuff
    return render_template('profile/about.html')

To create a blueprint object, we import the Blueprint() class and initialize it with the arguments name and import_name. Usually import_name will just be __name__, which is a special Python variable containing the name of the current module.

We're using a functional structure for this Facebook example. If we were using a divisional structure, we'd want to tell Flask that the blueprint has its own template and static directories. This code block shows what that would look like.

profile = Blueprint('profile', __name__,
                    template_folder='templates',
                    static_folder='static')

We have now defined our blueprint. It's time to register it on our Flask app.

# facebook/__init__.py

from flask import Flask
from .views.profile import profile

app = Flask(__name__)
app.register_blueprint(profile)

Now the routes defined in facebook/views/profile.py (e.g. /<user_url_slug>) are registered on the application and act just as if you'd defined them with @app.route().

7.4.2. Using a dynamic URL prefix

Continuing with the Facebook example, notice how all of the profile routes start with the <user_url_slug> portion and pass that value to the view. We want users to be able to access a profile by going to a URL like https://facebo-ok.com/john.doe. We can stop repeating ourselves by defining a dynamic prefix for all of the blueprint's routes.

Blueprints let us define both static and dynamic prefixes. We can tell Flask that all of the routes in a blueprint should be prefixed with /profile for example; that would be a static prefix. In the case of the Facebook example, the prefix is going to change based on which profile the user is viewing. Whatever text they choose is the URL slug of the profile which we should display; this is a dynamic prefix.

We have a choice to make when defining our prefix. We can define the prefix in one of two places: when we instantiate the Blueprint() class or when we register it with app.register_blueprint().

# facebook/views/profile.py

from flask import Blueprint, render_template

profile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>')

# [...]

# facebook/__init__.py

from flask import Flask
from .views.profile import profile

app = Flask(__name__)
app.register_blueprint(profile, url_prefix='/<user_url_slug>')

While there aren't any technical limitations to either method, it's nice to have the prefixes available in the same file as the registrations. This makes it easier to move things around from the top-level. For this reason, I recommend setting url_prefix on registration.

We can use converters to make the prefix dynamic, just like in route() calls. This includes any custom converters that we've defined. When using converters, we can pre-process the value given before handing it off to the view. In this case we'll want to grab the user object based on the URL slug passed into our profile blueprint. We'll do that by decorating a function with url_value_preprocessor().

# facebook/views/profile.py

from flask import Blueprint, render_template, g

from ..models import User

# The prefix is defined on registration in facebook/__init__.py.
profile = Blueprint('profile', __name__)

@profile.url_value_preprocessor
def get_profile_owner(endpoint, values):
    query = User.query.filter_by(url_slug=values.pop('user_url_slug'))
    g.profile_owner = query.first_or_404()

@profile.route('/')
def timeline():
    return render_template('profile/timeline.html')

@profile.route('/photos')
def photos():
    return render_template('profile/photos.html')

@profile.route('/about')
def about():
    return render_template('profile/about.html')

We're using the g object to store the profile owner and g is available in the Jinja2 template context. This means that for a barebones case all we have to do in the view is render the template. The information we need will be available in the template.

{# facebook/templates/profile/photos.html #}

{% extends "profile/layout.html" %}

{% for photo in g.profile_owner.photos.all() %}
    <img src="{{ photo.source_url }}" alt="{{ photo.alt_text }}" />
{% endfor %}

Note

  • The Flask documentation has a great tutorial on using prefixes for internationalizing your URLs.

7.4.3. Using a dynamic subdomain

Many SaaS (Software as a Service) applications these days provide users with a subdomain from which to access their software. Harvest, for example, is a time tracking application for consultants that gives you access to your dashboard from yourname.harvestapp.com. Here I'll show you how to get Flask to work with automatically generated subdomains like this.

For this section I'm going to use the example of an application that lets users create their own websites. Imagine that our app has three blueprints for distinct sections: the home page where users sign-up, the user administration panel where the user builds their website and the user's website. Since these three parts are relatively unconnected, we'll organize them in a divisional structure.

sitemaker/
    __init__.py
    home/
        __init__.py
        views.py
        templates/
            home/
        static/
            home/
    dash/
        __init__.py
        views.py
        templates/
            dash/
        static/
            dash/
    site/
        __init__.py
        views.py
        templates/
            site/
        static/
            site/
    models.py

This table explains the different blueprints in this app.

URL Route Description
sitemaker.com sitemaker/home Just a vanilla blueprint. Views, templates and static files for index.html, about.html and pricing.html.
bigdaddy.sitemaker.com sitemaker/site This blueprint uses a dynamic subdomain and includes the elements of the user's website. We'll go over some of the code used to implement this blueprint below.
bigdaddy.sitemaker.com/admin sitemaker/dash This blueprint could use both a dynamic subdomain and a URL prefix by combining the techniques in this section with those from the previous section.

We can define our dynamic subdomain the same way we defined our URL prefix. Both options (in the blueprint directory or in the top-level init.py) are available, but once again we'll keep the definitions in sitemaker/init.py.

# sitemaker/__init__.py

from flask import Flask
from .site import site

app = Flask(__name__)
app.register_blueprint(site, subdomain='<site_subdomain>')

Since we're using a divisional structure, we'll define the blueprint in sitema-ker/site/init.py.

# sitemaker/site/__init__py

from flask import Blueprint

from ..models import Site

# Note that the capitalized Site and the lowercase site
# are two completely separate variables. Site is a model
# and site is a blueprint.

site = Blueprint('site', __name__)

@site.url_value_preprocessor
def get_site(endpoint, values):
    query = Site.query.filter_by(subdomain=values.pop('site_subdomain'))
    g.site = query.first_or_404()

# Import the views after site has been defined. The views
# module will needto import 'site' so we need to make
# sure that we import views after site has been defined.
import .views

Now we have the site information from the database that we'll use to display the user's site to the visitor who requests their subdomain.

To get Flask to work with subdomains, we'll need to specify the SERVER_NAME configuration variable.

# config.py

SERVER_NAME = 'sitemaker.com'

Note A few minutes ago, as I was drafting this section, somebody in IRC said that their subdomains were working fine in development, but not in production. I asked if they had the SERVER_NAME configured, and it turned out that they had it in development but not production. Setting it in production solved their problem.

See the conversation between myself (imrobert in the log) and aplavin: http://dev.pocoo.org/irclogs/%23pocoo.2013-07-30.log

It was enough of a coincidence that I felt it warranted inclusion in the section.

Note You can set both a subdomain and url_prefix. Think about how we would configure the blueprint in sitemaker/dash with the URL structure from the table above.