Flask Blueprints

Part 1, Chapter 9

With tests in place, let's refactor the app, adding in Blueprints.

Unfamiliar with Blueprints? Check out the official Flask documentation. Essentially, they are self-contained components, used for encapsulating code, templates, and static files.

Create a new directory in "project" called "api", and add an __init__.py file along with users.py and models.py. Then within users.py add the following:

# services/users/project/api/users.py

from flask import Blueprint
from flask_restful import Resource, Api

users_blueprint = Blueprint('users', __name__)
api = Api(users_blueprint)

class UsersPing(Resource):
    def get(self):
        return {
        'status': 'success',
        'message': 'pong!'

api.add_resource(UsersPing, '/users/ping')

Here, we created a new instance of the Blueprint class and bound the UsersPing resource to it.

Then, add the following code to models.py:

# services/users/project/api/models.py

from sqlalchemy.sql import func

from project import db

class User(db.Model):

    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    username = db.Column(db.String(128), nullable=False)
    email = db.Column(db.String(128), nullable=False)
    active = db.Column(db.Boolean(), default=True, nullable=False)
    created_date = db.Column(db.DateTime, default=func.now(), nullable=False)

    def __init__(self, username, email):
        self.username = username
        self.email = email

Update project/__init__.py, removing the route and model and adding the Application Factory pattern:

# services/users/project/__init__.py

import os

from flask import Flask  # new
from flask_sqlalchemy import SQLAlchemy

# instantiate the db
db = SQLAlchemy()

# new
def create_app(script_info=None):

    # instantiate the app
    app = Flask(__name__)

    # set config
    app_settings = os.getenv('APP_SETTINGS')

    # set up extensions

    # register blueprints
    from project.api.users import users_blueprint

    # shell context for flask cli
    def ctx():
        return {'app': app, 'db': db}

    return app

Take note of the shell_context_processor. This is used to register the app and db to the shell. Now we can work with the application context and the database without having to import them directly into the shell, which you'll see shortly.

Update manage.py:

# services/users/manage.py

import sys
import unittest

from flask.cli import FlaskGroup

from project import create_app, db   # new
from project.api.models import User  # new

app = create_app()  # new
cli = FlaskGroup(create_app=create_app)  # new

def recreate_db():

def test():
    """Runs the tests without code coverage"""
    tests = unittest.TestLoader().discover('project/tests', pattern='test*.py')
    result = unittest.TextTestRunner(verbosity=2).run(tests)
    if result.wasSuccessful():
        return 0

if __name__ == '__main__':

Now, you can work with the app and db context directly:

$ docker-compose exec users flask shell

Python 3.7.2 (default, Mar  7 2019, 22:40:59)
[GCC 8.2.0] on linux
App: project [development]
Instance: /usr/src/app/instance

>>> app
<Flask 'project'>

>>> db
<SQLAlchemy engine=postgres://postgres:***@users-db:5432/users_dev>

>>> exit()

Update the imports at the top of project/tests/base.py and project/tests/test_config.py:

from project import create_app

app = create_app()

(import db as well in base.py)

Finally, remove the FLASK_APP environment variable from docker-compose.yml:

  - FLASK_ENV=development
  - APP_SETTINGS=project.config.DevelopmentConfig
  - DATABASE_URL=postgres://postgres:[email protected]:5432/users_dev
  - DATABASE_TEST_URL=postgres://postgres:[email protected]:5432/users_test


$ docker-compose up -d

$ docker-compose exec users python manage.py test

Apply the model to the dev database:

$ docker-compose exec users python manage.py recreate_db

Did this work? Let's hop into psql...

$ docker-compose exec users-db psql -U postgres

psql (11.2)
Type "help" for help.

postgres=# \c users_dev
You are now connected to database "users_dev" as user "postgres".

users_dev=# \dt
         List of relations
 Schema | Name  | Type  |  Owner
 public | users | table | postgres
(1 row)

users_dev=# \q

Correct any errors and move on.

Mark as Completed