Structuring Large Applications In Flask Using Blueprints

Structuring Large Applications In Flask Using Blueprints

Structuring Large Applications. Flask.

Flask is a wonderful framework. If you read my blog, you’ll realise just how much I love it. There are many reasons why I like Flask. The most important reason is the freedom it gives me. Freedom is power, but with great power, comes great responsibility.

What do I mean by freedom? Well, Flask is extremely flexible. Especially in comparison to a framework like Django. Django boxes you in. It provides a rigid structure and enforces it. One of the advantages is that it’s less prone to errors and bad application design.

I find this to be rather limiting, I like the freedom to structure my application however I want. This is where Flask shines. Flask gives you the keys to the kingdom and says “Do whatever you want”. Of course, this means you’re prone to making some errors in your structure, but that’s a good thing.

I know this sounds crazy, but these mistakes are vital in order to develop the right habits the hard way. When you finally uncover the secret to structuring Flask applications properly, you would have likely made several apps with varying structures until you get to the best practices. By then, you understand exactly why things should be structured the way they are.

Let’s discuss the 2 main ways to organise Flask applications.

The beginner’s method

The beginner’s method is something commonly seen in Flask tutorials. It’s no surprise either, it’s easy and straightforward. In most cases, it’s sufficient when demonstrating other Flask features where application structure doesn’t matter too much.

Here’s an example of such a structure:

from flask import Flask

app = Flask(__name__)
app.config.from_object('config.DevConfig')

# Website routes
@app.route('/')
def home():
    return "Home"

# API endpoints
@app.route('/api')
def api():
    return "API"

# Admin pages
@app.route('/admin')
def admin_home():
    return "Admin Home"

if __name__ == "__main__":
    app.run()

The directory structure for such an application might look something like this:

.
├── app.py
└── config.py

In the example above, we place all the routes in one file. Right now this doesn’t seem like a big deal. It’s only 3 routes, right? Well, we are talking about large applications in this article. In which case, this is only a tiny snapshot of how such an application could look. You could potentially have 20 routes in each of the 3 sections above.

Imagine that, 60 routes in one file! That’s unacceptable. It’s terrible design structure and I will explain exactly why.

  1. The file would get way too long. I don’t have a golden rule for how long a source code file should be. Of course, that depends on the project. However, needlessly long files make it exponentially harder to work with code especially when refactoring or debugging.

  2. Code reusability is almost non-existent.

  3. This file would be a nightmare to work on in a team setting. Can you imagine 5 people working directly on the same file? It’s the stuff of nightmares!

  4. Tying into the previous point, there is no modularity whatsoever as there is no separation of concerns.

That last point is rather interesting. What is the separation of concerns exactly? Ideally, when solving a problem, you’d break the problem down into sub-problems.

Each sub-problem is it’s own “concern”. When designing your software solution, each “concern” will be separated into its own module as far as business logic. Here’s a source for further reading on the subject.

So, when designing our application, we have to keep this concept in mind. It makes no sense to keep our API and admin routes in the same file. That being said, how do we fix it? Well, fear not, for Flask provides a very convenient way to achieve the modularisation that we want.

The right way (enter blueprints!)

Before we get into blueprints, let’s first discuss how to set up the project folder structure. When implementing modularization, it’s not enough to just separate logic into multiple files.

It’s a good idea to make sure each self-contained module is in its own folder, at least for the sake of organisation.

Here’s how we could improve the previous folder structure:

.
├── __init__.py
├── app
│   ├── __init__.py
│   ├── admin
│   │   ├── __init__.py

│   │   └── routes.py
│   ├── api
│   │   ├── __init__.py
│   │   └── routes.py
│   └── website
│       ├── __init__.py
│       └── routes.py
├── config.py
├── extensions.py
└── run.py

As you can see, we have a structure that looks more complex than the previous one. However, it’s actually simple once you understand it.

At the top level, we have a project root folder. This folder contains the configurations file (config.py). Read this article to understand Flask configuration setup. We have extensions.py which is where we will be initializing our Flask extensions for use in our blueprints. Finally, we have run.py, which is used to run the application. I’ll explain later why this file exists.

The __init__.py files in every directory allow python to recognise the folders as packages, which allows us to import modules from said folders.

We have an app folder. This is where all the application logic lives. The __init__.py file in this folder is where the app object is created, and where the blueprints are registered.

We then have 3 subfolders: admin, api and website. If you recall from our previous example, these are the three concerns of our app. We have __init__.py files in each of these folders so we can import the blueprints from them.

Flask blueprints are components that provide a means of extending a Flask application from a central point. Think of them as “sub-applications” in a larger application. Here are the official docs regarding blueprints.

Here’s how we create the admin blueprint that will handle admin pages:

from flask import Blueprint

admin = Blueprint('admin', __name__)

@admin.route("/")
def admin_home():
    return "<h1>Admin Home</h1>"

""" Add as many relevant routes as needed. Just like you'd do with an app object. """

The code above goes into routes.py in the admin package. We then do the same for each of the routes.py files in the other packages.

from flask import Blueprint

api = Blueprint('api', __name__)

@api.route("/")
def api_home():
    return "<h1>API Home</h1>"

""" Add as many relevant routes as needed. Just like you'd do with an app object. """

from flask import Blueprint, current_app

website = Blueprint('website', __name__)

@website.route('/')
def website_home():
    return "<h1>Website Home</h1>"

""" Add as many relevant routes as needed. Just like you'd do with an app object. """

Now, we’re ready to import these blueprints into our app. The application is created inside the __init__.py file in the app folder. This allows it to be imported directly from the app package.

Alternatively, you can create the app object in another file, but you’ll have to make sure you import the specific module within the app package if you choose to do so.

Here’s how our app/__init__.py file will look:

from flask import Flask

from .admin.routes import admin
from .api.routes import api
from .website.routes import website

from extensions import *

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.DevConfig')

    # Initialise extensions
    mongo.init_app(app)

    with app.app_context():
        app.register_blueprint(admin, url_prefix="/admin")
        app.register_blueprint(api, url_prefix="/api")
        app.register_blueprint(website)

    return app

What I’ve used here is a technique called the application factory method. I will write a dedicated article for that. It’s the preferred way to generate a Flask application. I’ll explain what’s happening in this file.

First, we import the blueprints from their respective packages. We then import extensions. We will initialize these later.

The create_app function is where all the magic happens. We initialize the flask application and then load the configuration settings. We then initialize the extensions. In case you’re wondering, the extensions.py file just contains instances of flask extensions. It looks like this:

from flask_pymongo import PyMongo

mongo = PyMongo()

In this example, it has only one extension but you can import as many as you like in this file. It’s important to initialize these extensions before registering the blueprints if you want to use the extensions in the blueprints.

We then register the blueprints. The url_prefix parameter is exactly what it sounds like. We have routes in the blueprints, the prefix will be what comes before the blueprint route in order to access the controller. In this example, “/admin/” will take the user to the admin route. This is completely optional. If you choose to leave this out, be careful to avoid route clashes.

We then return the app object to be run in a different file (run.py). This file looks like this:

from app import create_app

app = create_app()

if __name__ == "__main__":
    app.run()

I prefer to separate the application server from the application creation logic.

That’s a basic example of how to use blueprints to modularise your application logic and organise your large Flask applications. Although we haven’t built a massive application in this example, it is very extensible without breaking the established structure. If you have more concerns in your app, just create another package with a blueprint in the app folder, and register the blueprint.

Conclusion

The freedom Flask gives us allows us to structure our applications whichever way we want. This means that more discipline is required when building a Flask app, as it doesn’t hold your hand. Always make use of the mechanisms and extensions Flask provides to make your life easier. This is not only important at the start, but also in the future when maintaining the project.