Flask tutorials have an occasionally nasty habit of oversimplifying what actual Flask apps should look like. Many newcomers to Flask are likely under the misconception that having a single, giant app.py file is a totally normal and acceptable way to build an app. And why shouldn’t they? Almost every tutorial is structured this way, which is fine for simplicity’s sake, but it seems as though an asterisk is missing, which should read: *This is not a good way to build applications.

Good software is organized by separation of concerns. It doesn’t make much sense to keep logic associated with a marketing page in the same place as logic for an admin panel. Bathrooms and kitchens are separate rooms for a reason: one room is for shitting, and one room is for eating. It doesn’t make much sense to shit where you eat.

Flask has a built-in concept called Blueprints, which are a way for us to separate our app into parts which share very little common ground. There are a hundred reasons to utilize Blueprints in large apps, but your own sanity might be reason #1.

App Structure with Blueprints

The best way to get a glance at the benefits associated with using Blueprints is to see the effect they have on an app’s structure. First, let’s look at what a large app might look like without Blueprints.

Meet Billy. Billy is pretty new to Flask, but is confident in his abilities to build large-scale Flask apps after reading parts 1-5 in this series. Armed with only that knowledge, his app looks something like this:

my-app
├── /templates
├── /static
├── app.py
├── routes.py
├── models.py
├── config.py
├── requirements.txt
└── setup.py

Billy has a lot of what you might expect a lot first glance. He has his handy app.py file (ugh), a folder for templates, a folder for static assets, and so forth. Without knowing what Billy’s app does, this all seems reasonable. As it turns out, however, not knowing what the app does is our downfall in making such an assessment.

It turns out Billy is building a customer-facing service desk portal for Boeing. After rushing the Boeing 737 to market and murdering hundreds of people with criminal negligence, Boeing hired Billy to build a friendly service desk to field complaints from the families of victims. Service desks are complicated pieces of technology. Consider the major parts of what might go into something like Zendesk:

  • A public-facing landing page.
  • A logged-in experience for both customers and agents (Boeing employees).
  • Dashboards with performance metrics for admins.
  • An obscene amount of logic that isn’t even worth mentioning.

Billy’s app suddenly looks like the type of insult to engineering that only the creators of the Boeing 737 would commission. Is Billy really storing routes, templates, and assets for all these things in one place?

When we use Blueprints, we can encapsulate parts of our app into modules (AKA folders of Python files). Our portal’s landing page is going to use different logic, templates, and styles from our logged-in states. We also don’t need complex data visualization logic living anywhere except the page(s) where it belongs:

my-app
├── app.py
├── models.py
├── /landing
│   ├── /templates
│   ├── /static
│   ├── __init__.py
│   └── routes.py
├── /loggedin
│   ├── /templates
│   ├── /static
│   ├── __init__.py
│   └── routes.py
├── /charts
│   ├── /templates
│   ├── /static
│   ├── __init__.py
│   └── routes.py
├── config.py
├── requirements.txt
└── setup.py

Even without seeing the contents of any of these files, it's much more clear how this app is organized. At the top level, we have aspects of our app which are global: app.py is our entry point, models.py handles database models, and config.py is our config. Then, our app is separated into modules based on which parts of our app logically go together.

Did you notice how each module also has its own /templates and /static folders? When using Blueprints, we can separate these things accordingly. Now our landing page isn't loading irrelevant CSS which pertains to the admin, and vice versa. If we wanted, we could even have top-level /templates and /static folders which any of our modules could use.

If you happen to be a Django person, this may start to sound familiar. That's because we can equate Flask's Blueprints to Django's concept of apps. There are differences and added flexibility, but the concept remains the same.

Defining a Blueprint

Let's say we want to create a Blueprint for a custom admin panel we're building. First we'll create a directory in our Flask app called /admin. This folder will be where all routes, templates, and logic regarding admin stuff will live. Next we'll add a file called admin_routes.py which is where we'll define our Blueprint and create some routes for admin pages:

from flask import Blueprint, render_template
from flask import current_app as app


# Set up a Blueprint
admin_bp = Blueprint('admin_bp', __name__,
                     template_folder='templates',
                     static_folder='static')

Easy peasy! We're configuring our Blueprint as a variable named admin_bp (where bp means Blueprint - I thought of that one, you're welcome). The first parameter we pass is our Blueprint's name, which we're also naming admin_bp to stay consistent with our variable name.

The next argument we pass is a keyword argument called template_folder, which tells our Flask Blueprint which folder to render templates from in relation to the current file. Setting template_folder to templates means that our app will first look for a folder which matches /myapp/my-blueprint-folder/templates. If that folder doesn't exist, it will then try finding templates in /myapp/templates. This is important to remember- this behavior can get tricky very quickly.

Setting static_folder mimics the behavior of setting template_folder, except for serving static assets.

Creating Routes Inside a Blueprint

Now that we have a Blueprint created, we can start adding routes to this subsection of our app! Here's how that looks:

...

@admin_bp.route('/admin', methods=['GET'])
def admin():
    """Admin page route."""
    return render_template('admin.html')

The only notable difference here is that we now register our route by using @admin_bp.route(...) instead of @app.route(...). Creating our admin_bp Blueprint automatically gives us a decorator function called @admin_bp with which we can register our routes to.

Registering Our Blueprint

We've created a Blueprint, but we haven't told our Flask app to recognize it yet. We created a file called routes.py, but we haven't imported or registered the contents of that file yet.

This is where we break into our top-level entry point, like app.py (or preferably the _init_ file of an app using the application factory). Here's the simplest example of registering a Blueprint imaginable:

from flask import Flask
from .admin import admin_routes


app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
app.register_blueprint(admin_routes.admin_bp)

We start off by importing our admin_routes file form the /admin folder. The next two lines should be familiar: these are simply creating out Flask app and setting some configurations.

The last line is the most important: this is where we call register_blueprint() on our app object to tell Flask that a Blueprint exists here! We specify that our Blueprint is the admin_bp variable found in the admin_routes by passing admin_routes.admin_bp. When we do this, Flask will then recognize any routes which were created as a part of the admin_bp Blueprint, like the route we created for our app's landing page!

We'll almost certainly have more than a single Blueprint, since modularizing our app is worthless if we only have one module. Registering multiple Blueprints is simple:

from flask import Flask
from .admin import admin_routes
from .main import main_routes


app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
app.register_blueprint(admin_routes.admin_bp)
app.register_blueprint(main_routes.main_bp)

Now we have admin pages as well as main (non-admin) pages.

Jinja Templates & Blueprints

That last caveat we need to be aware of is how Jinja templates find URLs for routes registered to Blueprints. Let's say we want to create a link to our app's homepage. Here's what you're probably used to seeing:

<a href="{{ url_for('home') }}">Home</a>

The above Jinja snippet looks for a route named home registered to our app. In our app, we don't register routes directly to the Flask app- we register routes to each Blueprint. The above code wouldn't work for us.

Instead, let's check our main Blueprint for a route named home:

<a href="{{ url_for('main_bp.home') }}">Home</a>

Blueprints In Action

To drive the concept of Blueprints home, it's worth taking a look at how a fleshed-out example would look. I created an example app for us to see exactly what a working Flask app with Blueprints might look like (the source code can be found here). Here's our project structure:

flaskblueprint-tutorial
├── /application
│   ├── __init__.py
│   ├── /admin
│   │   ├── __init__.py
│   │   ├── admin_routes.py
│   │   ├── assets.py
│   │   └── /templates
│   ├── /main
│   │   ├── __init__.py
│   │   ├── assets.py
│   │   ├── main_routes.py
│   │   └── /templates
│   ├── /static
│   └── /templates
│       ├── layout.html
│       ├── meta.html
│       ├── navigation-default.html
│       ├── navigation-loggedin.html
│       └── scripts.html
├── config.py
├── requirements.txt
├── start.sh
└── wsgi.py

We have two directories inside our application: /admin and /main. These make up the two Blueprints in our app: one for an admin panel, and one for everything else.

If you pay attention to the templates, you'll notice something interesting happening here. Each of our Blueprints has its own directory of Blueprint-specific Jinja templates, but then there's a /templates directory in our application root as well! If you remember the logic of setting a template_folder with a Blueprint, Flask will fall back to a global templates folder when the template in question doesn't exist in a Blueprint. That means we can share partials and layouts between Blueprints!

This example is a pretty good representation of what a very basic app using Blueprints correctly would look like. If you're having trouble getting Blueprints to work, I'd urge you to take a look at the Github repository containing this source code.

Farewell

I realize that this is all easier said than done. If you're new to creating Flask apps with an application factory, the above probably looks like a bunch of gibberish. Don't worry: it kind of is. Creating apps in this way is a topic in itself, which we cover separately. Otherwise, you have all you need to know to start building logical, organized Flask apps. Godspeed, and screw Boeing.