Organizing Flask Apps with Blueprints

Introduce structure to your Flask app by utilizing Flask's Blueprints.

Organizing Flask Apps with Blueprints

    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

    Well then, how do we define a Blueprint? I'm glad you asked! Let's say we want to create a Blueprint for an admin panel we're building. We'll name our directory /admin, and we'll modify routes.py (or whatever you want) as such:

    from flask import Blueprint, render_template
    from flask import current_app as app
    
    
    # Blueprint Configuration
    admin_bp = Blueprint('admin_bp', __name__,
                         template_folder='templates',
                         static_folder='static')
    
    
    @main_bp.route('/admin', methods=['GET'])
    def admin():
        """Admin page route."""
        return render_template('admin.html')
    

    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 positional we pass is our Blueprint's name. We also set template_folder and static_folder here, which is the relative path to the where our templates and static assets will live in relation to the current file. This is how we get Blueprints like Billy's, where each Blueprint has its own stuff.

    BONUS: Blueprint-specific Assets with Flask-Assets

    Want to have Blueprint-specific asset bundles, which contain your JS and CSS? No problem! We can modify what we’ve already got to accommodate this. I won’t waste time going into detail as we’ve done so in a previous post, but creating asset bundles around Blueprints is important to the concept:

    from flask import Blueprint, render_template
    from flask_assets import Environment, Bundle
    from flask import current_app as app
    
    
    # Flask-Assets Configuration
    assets = Environment(app)
    Environment.auto_build = True
    less_bundle = Bundle('src/less/dashboard.less',
                         filters='less,cssmin',
                         output='dist/css/dashboard.css',
                         extra={'rel': 'stylesheet/less'})
    assets.register('less_all', less_bundle)
    less_bundle.build()
    
    # Blueprint Configuration
    admin_bp = Blueprint('admin_bp', __name__,
                         template_folder='templates',
                         static_folder='static')
    
    
    @main_bp.route('/admin', methods=['GET'])
    def admin():
        """Admin page route."""
        return render_template('admin.html')
    

    Register Our Blueprint

    We're not done yet, fella. We've created a Blueprint, but we haven't told Flask to recognize it 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. Let's pretend like we're in Billy's app again:

    from flask import Flask
    from flask_sqlalchemy import SQLAlchemy
    from flask_login import LoginManager
    
    
    db = SQLAlchemy()
    login_manager = LoginManager()
    
    
    def create_app():
        """Construct the core application."""
        app = Flask(__name__, instance_relative_config=False)
    
        # Application Configuration
        app.config.from_object('config.Config')
    
        # Initialize Plugins
        db.init_app(app)
        login_manager.init_app(app)
    
        with app.app_context():
            # Import Blueprints
            app.register_blueprint(landing.landing_bp)
            app.register_blueprint(admin.admin_bp)
            app.register_blueprint(charts.chart_bp)
    
            # Create Database Models
            db.create_all()
    
            return app
    

    I've included a few things above to make our situation realistic (db = SQLAlchemy() and login_manager = LoginManager()). Feel free to ignore these: that's not what we're focusing on.

    The notable pieces are the 3 Blueprints we register: landing_bp, admin_bp, and charts_bp. After creating Blueprints the way we created admin_bp, we simply need to make sure our app recognizes them in our app's entry point, within the app_context().

    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 top 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.

    Todd Birchard's' avatar
    New York City Website
    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.

    Product manager turned engineer with an ongoing identity crisis. Breaks everything before learning best practices. Completely normal and emotionally stable.