Creating a RESTful API with Flask

Creating a RESTful API with Flask

A RESTful API is an application program interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. In this tutorial, we'll be learning and creating a RESTful APIs with Flask.

A RESTful API is an application program interface (API) that uses HTTP requests to GET, PUT, POST and DELETE data. In this tutorial, we'll be learning and creating a RESTful APIs with Flask.

This article’s intention is to provide a easy-to-follow project-based process on how to create a RESTful API using the Flask framework.

Why Flask?

A bit of context – I've written a bunch of articles on Django-driven RESTful APIs. Though a great resource for Django enthusiasts, not everyone wants to code in Django. Besides, it's always good to acquaint yourself with other frameworks.

Learning Flask is easier and faster. It's super easy to setup and get things running. Unlike Django (which is heavier), you'll never have functionality lying around that you aren't using.

Typical of all our web apps, we'll use the TDD approach. It's really simple. Here's how we do Test Driven Development:

  • Write a test. – The test will help flesh out some functionality in our app
  • Then, run the test – The test should fail, since there's no code(yet) to make it pass.
  • Write the code – To make the test pass
  • Run the test – If it passes, we are confident that the code we've written meets the test requirements
  • Refactor code – Remove duplication, prune large objects and make the code more readable. Re-run the tests every time we refactor our code
  • Repeat – That's it!
What we'll create

We're going to develop an API for a bucketlist. A bucketlist is a list of all the goals you want to achieve, dreams you want to fulfill and life experiences you desire to experience before you die (or hit the bucket). The API shall therefore have the ability to:

  • Create a bucketlist (by giving it a name/title)
  • Retrieve an existing bucketlist
  • Update it (by changing it's name/title)
  • Delete an existing bucketlist

Prerequisites

  • Python3 - A programming language that lets us work more quickly (The universe loves speed!).
  • Flask - A microframework for Python based on Werkzeug, Jinja 2 and good intentions
  • Virtualenv - A tool to create isolated virtual environments

Let's start with configuring our Flask app structure!

Virtual Environment

First, we'll create our application directory. On the terminal, create an empty directory called bucketlist with mkdir bucketlist. Then, Cd into the directory. Create an isolated virtual environment:

$ virtualenv venv

Install Autoenv globally using pip install autoenv Here's why – Autoenv helps us to set commands that will run every time we cd into our directory. It reads the .env file and executes for us whatever is in there.

Create a .env file and add the following:

source env/bin/activate
export FLASK_APP="run.py"
export SECRET="some-very-long-string-of-random-characters-CHANGE-TO-YOUR-LIKING"
export APP_SETTINGS="development"
export DATABASE_URL="postgresql://localhost/flask_api"

The first line activates our virtual environment venv that we just created. Line 2, 3 and 4 export our FLASK_APP, SECRET, APP_SETTINGS and DATABASE_URL variables. We'll integrate these variables as we progress through the development process.

The first line activates our virtual environment venv that we just created. Line 2, 3 and 4 export our FLASK_APP, SECRET, APP_SETTINGS and DATABASE_URL variables. We'll integrate these variables as we progress through the development process.

Run the following to update and refresh your .bashrc:

$ echo "source `which activate.sh`" >> ~/.bashrc
$ source ~/.bashrc

You will see something like this on your terminal

Sometimes autoenv might not work if you have zsh installed. A good workaround would be to simply source the .env file and we are set.

$ source .env

Conversely, if you don't want to automate things for the long run, you don't have to use autoenv. A simple export directly from the terminal would do.

$ export FLASK_APP="run.py"
$ export APP_SETTINGS="development"
$ export SECRET="a-long-string-of-random-characters-CHANGE-TO-YOUR-LIKING"
$ export DATABASE_URL="postgresql://localhost/flask_api"

Inside our virtual environment, we'll create a bunch of files to lay out our app directory stucture. Here's what it should look like:

├── bucketlist (this is the directory we cd into)
    ├── app
    │   ├── __init__.py
    │   └── models.py  
    ├── instance
    │   └── __init__.py
    ├── manage.py
    ├── requirements.txt
    ├── run.py
    └── test_bucketlist.py

After doing this, install Flask using pip.

(venv)$ pip install flask

Environment Configurations

Flask needs some sought of configuration to be available before the app starts. Since environments (development, production or testing) require specific settings to be configured, we'll have to set environment-specific things such as a secret key, debug mode and test mode in our configurations file.

If you haven't already, create a directory and call it instance. Inside this directory, create a file called config.py and also init.py. Inside our config file, we'll add the following code:

# /instance/config.py

import os


class Config(object):
    """Parent configuration class."""
    DEBUG = False
    CSRF_ENABLED = True
    SECRET = os.getenv('SECRET')
    SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL')


class DevelopmentConfig(Config):
    """Configurations for Development."""
    DEBUG = True


class TestingConfig(Config):
    """Configurations for Testing, with a separate test database."""
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'postgresql://localhost/test_db'
    DEBUG = True

class StagingConfig(Config):
    """Configurations for Staging."""
    DEBUG = True


class ProductionConfig(Config):
    """Configurations for Production."""
    DEBUG = False
    TESTING = False


app_config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'staging': StagingConfig,
    'production': ProductionConfig,
}

The Config class contains the general settings that we want all environments to have by default. Other environment classes inherit from it and can be used to set settings that are only unique to them. Additionally, the dictionary app_config is used to export the 4 environments we've specified. It's convenient to have it so that we can import the config under its name tag in future.

A couple of config variables to note:

  • The SECRET_KEY – is a random string of characters that's used to generate hashes that secure various things in an app. It should never be public to prevent malicious attackers from accessing it.
  • DEBUG – tells the app to run under debugging mode when set to True, allowing us to use the Flask debugger. It also automagically reloads the application when it's updated. However, it should be set to False in production.
Configuring the Database

Installation requirements

The tools we need for our database to be up and running are:

We might have used a easy-to-setup database such as SQLite. But since we want to learn something new, powerful and awesome, we'll go with PostgreSQL.

SQLAlchemy is our Object Relational Mapper (ORM). Why should we use an ORM, you ask? An ORM converts the raw SQL data (called querysets) into data we can understand called objects in a process called serialization and vice versa (deserialization). Instead of painstakingly writing complex raw SQL queries, why not use a tested tool developed just for this purpose?

Let's install the requirements as follows:

(venv)$ pip install flask-sqlalchemy psycopg2 flask-migrate

Ensure you have installed PostgresSQL in your computer and it's server is running locally on port 5432

In your terminal, create a Postgres database:

(venv) $ createdb test_db
(venv) $ createdb flask_api

Createdb is a wrapper around the SQL command CREATE DATABASE. We created

  • test database test_db for our testing environment.
  • main database flask_api for development environment.

We've used two databases so that we do not interfere with the integrity of our main database when running our tests.

Create The App

It's time to right some code! Since we are creating an API, we'll install Flask API extension.

(venv)$ pip install Flask-API

Flask API is an implementation of the same web browsable APIs that Django REST framework provides. It'll helps us implement our own browsable API.

In our empty app/__init__.py file, we'll add the following:

# app/__init__.py

from flask_api import FlaskAPI
from flask_sqlalchemy import SQLAlchemy

# local import
from instance.config import app_config

# initialize sql-alchemy
db = SQLAlchemy()


def create_app(config_name):
    app = FlaskAPI(__name__, instance_relative_config=True)
    app.config.from_object(app_config[config_name])
    app.config.from_pyfile('config.py')
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    db.init_app(app)

    return app

The create_app function wraps the creation of a new Flask object, and returns it after it's loaded up with configuration settings using app.config and connected to the DB using db.init_app(app).

We've also disabled track modifications for SQLAlchemy because it'll be deprecated in future due to it's significant performance overhead. For debugging enthusiasts, you can set it to True for now.

Now, we need to define an entry point to start our app. Let's edit the run.py file.

import os

from app import create_app

config_name = os.getenv('APP_SETTINGS') # config_name = "development"
app = create_app(config_name)

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

Run it!

Now we can run the application on our terminal to see if it works:

(venv)$ flask run

We can also run it using python run.py. We should see something like this:

Data Model

It's time to create our bucketlist model. A model is a representation of a table in a database. Add the following inside the empty models.py file:

# app/models.py

from app import db


class Bucketlist(db.Model):
    """This class represents the bucketlist table."""

    __tablename__ = 'bucketlists'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(
        db.DateTime, default=db.func.current_timestamp(),
        onupdate=db.func.current_timestamp())

    def __init__(self, name):
        """initialize with name."""
        self.name = name

    def save(self):
        db.session.add(self)
        db.session.commit()

    @staticmethod
    def get_all():
        return Bucketlist.query.all()

    def delete(self):
        db.session.delete(self)
        db.session.commit()

    def __repr__(self):
        return "".format(self.name)

Here's what we've done in the models.py file:

  • We imported our db connection from the app/__init__.py.
  • Next, we created a Bucketlist class that inherits from db.Model and assigned a table. name bucketlists (it should always be plural). We've therefore created a table to store our bucketlists.
  • The id field contains the primary key, the name field will store the name of the bucketlist.
  • The __repr__ method represents the object instance of the model whenever it is queries.
  • The get_all() method is a static method that'll be used to get all the bucketlists in a single query.
  • The save() method will be used to add a new bucketlist to the DB.
  • The delete() method will be used to delete an existing bucketlist from the DB.
Making Migrations

Migrations is a way of propagating changes we make to our models (like adding a field, deleting a model, etc.) into your database schema. Now that we've a defined model in place, we need to tell the database to create the relevant schema.

Flask-Migrate uses Alembic to autogenerate migrations for us. It will serve this purpose.

The migration script

A migration script will conveniently help us make and apply migrations everytime we edit our models. It's good practice to separate migration tasks and not mix them with the code in our app.

That said, we'll create a new file called manage.py.

Our directory structure should now look like this:

├── bucketlist
    ├── app
    │   ├── __init__.py
    │   └── models.py  
    ├── instance
    │   ├── __init__.py
    │   └── config.py
    ├── manage.py
    ├── requirements.txt
    ├── run.py
    └── test_bucketlist.py

Add the following code to manage.py:

# manage.py

import os
from flask_script import Manager # class for handling a set of commands
from flask_migrate import Migrate, MigrateCommand
from app import db, create_app
from app import models


app = create_app(config_name=os.getenv('APP_SETTINGS'))
migrate = Migrate(app, db)
manager = Manager(app)

manager.add_command('db', MigrateCommand)


if __name__ == '__main__':
    manager.run()

The Manager class keeps track of all the commands and handles how they are called from the command line. The MigrateCommand contains a set of migration commands. We've also imported the models so that the script can find the models to be migrated. The manager also adds the migration commands and enforces that they start with db.

We will run migrations initialization, using the db init command as follows:

(venv)$ python manage.py db init

You'll notice a newly created folder called migrations. This holds the setup necessary for running migrations. Inside of “migrations” is a folder called “versions”, which will contain the migration scripts as they are created.

Next, we'll run the actual migrations using the db migrate command:

 (venv)$   python manage.py db migrate

  INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
  INFO  [alembic.runtime.migration] Will assume transactional DDL.
  INFO  [alembic.autogenerate.compare] Detected added table 'results'
    Generating /bucketlist/migrations/versions/63dba2060f71_.py
    ...done

You’ll also notice that in your versions folder there is a migration file. This file is auto-generated by Alembic based on the model.

Finally, we’ll apply the migrations to the database using the db upgrade command:

(venv)$   python manage.py db upgrade

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 536e84635828, empty message

Our DB is now updated with our bucketlists table. If you jump into the psql prompt, here's a screenshot on how you can confirm if the table exists:

Time To Test!

Inside our tests directory, let's create tests. Creating tests that fail is the first step of TD.(Failing is good). These tests will help guide us in creating our functionality. It might seem daunting at first to write tests but it's really easy once you get practicing.

On the parent directory, create a test file called test_bucketlist.py. This file will contain the following:

  • Test Case class to house all our API tests.
  • setUp() methods to initialize our app and it's test client and create our test database within the app's context.
  • tearDown() method to tear down test variables and delete our test database after testing is done.
  • tests to test whether our API can create, read, update and delete a bucketlist.
# test_bucketlist.py
import unittest
import os
import json
from app import create_app, db


class BucketlistTestCase(unittest.TestCase):
    """This class represents the bucketlist test case"""

    def setUp(self):
        """Define test variables and initialize app."""
        self.app = create_app(config_name="testing")
        self.client = self.app.test_client
        self.bucketlist = {'name': 'Go to Borabora for vacation'}

        # binds the app to the current context
        with self.app.app_context():
            # create all tables
            db.create_all()

    def test_bucketlist_creation(self):
        """Test API can create a bucketlist (POST request)"""
        res = self.client().post('/bucketlists/', data=self.bucketlist)
        self.assertEqual(res.status_code, 201)
        self.assertIn('Go to Borabora', str(res.data))

    def test_api_can_get_all_bucketlists(self):
        """Test API can get a bucketlist (GET request)."""
        res = self.client().post('/bucketlists/', data=self.bucketlist)
        self.assertEqual(res.status_code, 201)
        res = self.client().get('/bucketlists/')
        self.assertEqual(res.status_code, 200)
        self.assertIn('Go to Borabora', str(res.data))

    def test_api_can_get_bucketlist_by_id(self):
        """Test API can get a single bucketlist by using it's id."""
        rv = self.client().post('/bucketlists/', data=self.bucketlist)
        self.assertEqual(rv.status_code, 201)
        result_in_json = json.loads(rv.data.decode('utf-8').replace("'", "\""))
        result = self.client().get(
            '/bucketlists/{}'.format(result_in_json['id']))
        self.assertEqual(result.status_code, 200)
        self.assertIn('Go to Borabora', str(result.data))

    def test_bucketlist_can_be_edited(self):
        """Test API can edit an existing bucketlist. (PUT request)"""
        rv = self.client().post(
            '/bucketlists/',
            data={'name': 'Eat, pray and love'})
        self.assertEqual(rv.status_code, 201)
        rv = self.client().put(
            '/bucketlists/1',
            data={
                "name": "Dont just eat, but also pray and love :-)"
            })
        self.assertEqual(rv.status_code, 200)
        results = self.client().get('/bucketlists/1')
        self.assertIn('Dont just eat', str(results.data))

    def test_bucketlist_deletion(self):
        """Test API can delete an existing bucketlist. (DELETE request)."""
        rv = self.client().post(
            '/bucketlists/',
            data={'name': 'Eat, pray and love'})
        self.assertEqual(rv.status_code, 201)
        res = self.client().delete('/bucketlists/1')
        self.assertEqual(res.status_code, 200)
        # Test to see if it exists, should return a 404
        result = self.client().get('/bucketlists/1')
        self.assertEqual(result.status_code, 404)

    def tearDown(self):
        """teardown all initialized variables."""
        with self.app.app_context():
            # drop all tables
            db.session.remove()
            db.drop_all()


# Make the tests conveniently executable
if __name__ == "__main__":
    unittest.main()

A bit of testing explanation. Inside the test_bucketlist_creation(self) we make a POST request using a test client to the /bucketlists/ url. The return value is obtained and its status code is asserted to be equal to a status code of 201(Created). If it's equal to 201, the test assertion is true, making the test pass. Finally, it checks whether the returned response contains the name of the bucketlist we just created. This is done using self.assertIn(a, b) If the assertion evaluates to true, the test passes.

Now we'll run the test as follows:

(venv)$ python test_bucketlist.py

All the tests must fail. Now don't be scared. This is good because we have no functionality to make the test pass. Now's the time to create the API functionality that will make our tests pass.

API Functionality

Our API is supposed to handle four HTTP requests

  • POST – Used to create the bucketlist
  • GET – For retrieving one bucketlist using its ID and many bucketlists
  • PUT – For updating a bucketlist given its ID
  • DELETE – For deleting a bucketlist given its ID

Let's get this done straight away. Inside our app/__init__.py file, we'll edit it as follows:

# app/__init__.py

# existing import remains

from flask import request, jsonify, abort

def create_app(config_name):
    from api.models import Bucketlist

    #####################
    # existing code remains #
    #####################

    @app.route('/bucketlists/', methods=['POST', 'GET'])
    def bucketlists():
        if request.method == "POST":
            name = str(request.data.get('name', ''))
            if name:
                bucketlist = Bucketlist(name=name)
                bucketlist.save()
                response = jsonify({
                    'id': bucketlist.id,
                    'name': bucketlist.name,
                    'date_created': bucketlist.date_created,
                    'date_modified': bucketlist.date_modified
                })
                response.status_code = 201
                return response
        else:
            # GET
            bucketlists = Bucketlist.get_all()
            results = []

            for bucketlist in bucketlists:
                obj = {
                    'id': bucketlist.id,
                    'name': bucketlist.name,
                    'date_created': bucketlist.date_created,
                    'date_modified': bucketlist.date_modified
                }
                results.append(obj)
            response = jsonify(results)
            response.status_code = 200
            return response

    return app

We've imported

  • request for handling our requests.
  • jsonify to turn the JSON output into a Response object with the application/json mimetype.
  • abort which will abort a request with an HTTP error code early.

We've also added an import from api.models import Bucketlist immediately inside the create_app method so that we get access to the Bucketlist model while preventing the horror of circular imports. Flask provides an @app.route decorator on top of the new function def bucketlists() which enforces us to only accepts GET and POST requests. Our function first checks the type of request it receives. If it's a POST, it creates a bucketlist by extracting the name from the request and saves it using the save() method we defined in our model. It consequently returns the newly created bucketlist as a JSON object. If it's a GET request, it gets all the bucketlists from the bucketlists table and returns a list of bucketlists as JSON objects. If there's no bucketlist on our table, it will return an empty JSON object {}.

Now let's see if our new GET and POST functionality makes our tests pass.

Running our Tests

Run the tests as follows:

(venv)$ python test_bucketlists.py

2 out of 5 tests should pass. We've now handled the GET and POST requests successfully.

At this moment, our API can only create and get all the bucketlists. It cannot get a single bucketlist using its bucketlist ID. Also, it can neither edit a bucketlist nor delete it from the DB. To complete it, we'd want to add these functionalities.

Adding PUT and DELETE functionality

On our app/__init__.py file, let's edit as follows:

# app/__init__.py

# existing import remains

def create_app(config_name):

    #####################
    # existing code remains #
    #####################

    ###################################
    # The GET and POST code is here
    ###################################

    @app.route('/bucketlists/', methods=['GET', 'PUT', 'DELETE'])
    def bucketlist_manipulation(id, **kwargs):
     # retrieve a buckelist using it's ID
        bucketlist = Bucketlist.query.filter_by(id=id).first()
        if not bucketlist:
            # Raise an HTTPException with a 404 not found status code
            abort(404)

        if request.method == 'DELETE':
            bucketlist.delete()
            return {
            "message": "bucketlist {} deleted successfully".format(bucketlist.id) 
         }, 200

        elif request.method == 'PUT':
            name = str(request.data.get('name', ''))
            bucketlist.name = name
            bucketlist.save()
            response = jsonify({
                'id': bucketlist.id,
                'name': bucketlist.name,
                'date_created': bucketlist.date_created,
                'date_modified': bucketlist.date_modified
            })
            response.status_code = 200
            return response
        else:
            # GET
            response = jsonify({
                'id': bucketlist.id,
                'name': bucketlist.name,
                'date_created': bucketlist.date_created,
                'date_modified': bucketlist.date_modified
            })
            response.status_code = 200
            return response

    return app

We've now defined a new function def bucketlist_manipulation() which uses a decorator that enforces it to only handle GET, PUT and DELETE Http requests. We query the db to filter using an id of the given bucketlist we want to access. If there's no bucketlist, it aborts and returns a 404 Not Found status. The second if-elif-else code blocks handle deleting, updating or getting a bucketlist respectively.

Run the tests

Now, we expect all the tests to pass. Let's run them and see if all of them actually pass.

(venv)$ python test_bucketlist.py

We should now see all the test passing.

Test using Postman and Curl

Fire up Postman. Key in [http://localhost:5000/bucketlists/](http://localhost:5000/bucketlists/) and send a POST request with a name as the payload. We should get a response like this:

We can play around with Curl as well to see it working from our terminal:

The User Model

We intend to allow bucketlists to be owned by users. For now, anyone can manipulate a bucketlist even if they did not create it. We've got to fix this security hole.

How do we keep track of users, you ask? We define a model.

# app/models.py

from app import db
from flask_bcrypt import Bcrypt

class User(db.Model):
    """This class defines the users table """

    __tablename__ = 'users'

    # Define the columns of the users table, starting with the primary key
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(256), nullable=False, unique=True)
    password = db.Column(db.String(256), nullable=False)
    bucketlists = db.relationship(
        'Bucketlist', order_by='Bucketlist.id', cascade="all, delete-orphan")

    def __init__(self, email, password):
        """Initialize the user with an email and a password."""
        self.email = email
        self.password = Bcrypt().generate_password_hash(password).decode()

    def password_is_valid(self, password):
        """
        Checks the password against it's hash to validates the user's password
        """
        return Bcrypt().check_password_hash(self.password, password)

    def save(self):
        """Save a user to the database.
        This includes creating a new user and editing one.
        """
        db.session.add(self)
        db.session.commit()

class Bucketlist(db.Model):
    """This class defines the bucketlist table."""

    __tablename__ = 'bucketlists'

    # define the columns of the table, starting with its primary key
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
    date_modified = db.Column(
        db.DateTime, default=db.func.current_timestamp(),
        onupdate=db.func.current_timestamp())
    created_by = db.Column(db.Integer, db.ForeignKey(User.id))

    def __init__(self, name, created_by):
        """Initialize the bucketlist with a name and its creator."""
        self.name = name
        self.created_by = created_by

    def save(self):
        """Save a bucketlist.
        This applies for both creating a new bucketlist
        and updating an existing onupdate
        """
        db.session.add(self)
        db.session.commit()

    @staticmethod
    def get_all(user_id):
        """This method gets all the bucketlists for a given user."""
        return Bucketlist.query.filter_by(created_by=user_id)

    def delete(self):
        """Deletes a given bucketlist."""
        db.session.delete(self)
        db.session.commit()

    def __repr__(self):
        """Return a representation of a bucketlist instance."""
        return "".format(self.name)

Here's what we've done:

  • We imported Flask-Bcrypt extension to help us in hashing our passwords. You should never store passwords in plaintext.
  • We created a User model that represents the users table. It contains the email and password fields to capture the user's credentials.
  • Since a user can own many bucketlists, we defined a One-to-Many relationship between the two tables. We defined this relationship by adding the db.relationship() function on the User table (parent table)
  • We added a foreign key on the child table (Bucketlist) referencing the User table. The foreign key has some arguments. The cascade="all, delete-orphan" will delete all bucketlists when a referenced user is deleted.
  • We hash the password by using generate_password_hash(pasword). This will make our users password be secure from dictionary and brute force attacks.
  • We refactored the get_all() method to get all the bucketlists for a given user.

Don't forget to install Flask-Bcrypt

(venv)$ pip install flask-bcrypt 

Migrate them

Migrate the changes we've just made to the db

(venv)$    python manage.py db migrate
(venv)$    python manage.py db upgrade

Now we have a user table to keep track of registered users.

Automate Tests

Our app will have many tests from now on. It's best practice to have a test folder that will houses all our tests. We'll create a folder called tests. Inside this folder, we'll move our test_bucketlists.py file into it.

Our directory structure should now look like this:

├── bucketlist
    ├── app
    │   ├── __init__.py
    │   └── models.py  
    ├── instance
    │   ├── __init__.py
    │   └── config.py
    ├── manage.py
    ├── requirements.txt
    ├── run.py
    ├── tests
    │   └── test_bucketlist.py

Also, we'll edit the manage.py as follows:

import os
import unittest
# class for handling a set of commands
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app import db, create_app

# initialize the app with all its configurations
app = create_app(config_name=os.getenv('APP_SETTINGS'))
migrate = Migrate(app, db)
# create an instance of class that will handle our commands
manager = Manager(app)

# Define the migration command to always be preceded by the word "db"
# Example usage: python manage.py db init
manager.add_command('db', MigrateCommand)

# define our command for testing called "test"
# Usage: python manage.py test
@manager.command
def test():
    """Runs the unit tests without test coverage."""
    tests = unittest.TestLoader().discover('./tests', pattern='test*.py')
    result = unittest.TextTestRunner(verbosity=2).run(tests)
    if result.wasSuccessful():
        return 0
    return 1

if __name__ == '__main__':
    manager.run()

The decorator on top of test() allows us to define a command called test. Inside the function, we load the tests from the tests folder using the TestLoader() class and then run them with TextTestRunner.run(). If it's successful, we exit gracefully with a return 0.

Let's test it out on our terminal.

(venv)$ python manage.py test 

The tests should fail. This is because we've not modified our code to work with the new changes in the model.

From now on, we'll use this command to run our tests.

Token-based authentication

Token-based authentication is a security technique that authenticates users who attempt to login to a server using a security token provided by the server. Without the token, a user won’t be granted access to restricted resources. You can find more intricate details about token based authentication here

For us to implement this authentication, we’ll use a Python package called PyJWT.

PyJWT allows us to encode and decode JSON Web Tokens (JWT). That being said, let’s install it:

(venv)$ pip install PyJWT 

Securing Requests

For our users to authenticate, the access token is going to be placed in the Authorization HTTP header in all our bucketlist requests.

Here's how the header looks like:

Authorization: "Bearer <The-access-token-is-here>" 

We'll put the word Bearer before the token and separate them with a space character.

Don't forget the space in between the Bearer and the token.

Encode and Decode the Token

We need to create a way to encode the token before it's sent to the user. We also need to have a way to decode the token when the user sends it via the Authorization header.

In our model.py we'll create a function inside our User model to generate the token and another one to decode it. Let's add the following code:

# /app/models.py

## previous  imports ###
import jwt
from datetime import datetime, timedelta

class User(db.Model):
    """Maps to users table """

    __tablename__ = &apos;users&apos;

    ###########################################
    ## Existing code for defining table columns is here  ##
    ###########################################

    def __init__(self, email, password):
        #### INIT CODE LIES HERE ###################
        ###########################################

    def password_is_valid(self, password):
        ##### PASSWORD CHECK CODE LIES HERE ####
        ###########################################

    def save(self):
        ######### CODE FOR SAVING USER LIES HERE ##
        ############################################

    def generate_token(self, user_id):
        """ Generates the access token"""

        try:
            # set up a payload with an expiration time
            payload = {
                &apos;exp&apos;: datetime.utcnow() + timedelta(minutes=5),
                &apos;iat&apos;: datetime.utcnow(),
                &apos;sub&apos;: user_id
            }
            # create the byte string token using the payload and the SECRET key
            jwt_string = jwt.encode(
                payload,
                current_app.config.get(&apos;SECRET&apos;),
                algorithm=&apos;HS256&apos;
            )
            return jwt_string

        except Exception as e:
            # return an error in string format if an exception occurs
            return str(e)

    @staticmethod
    def decode_token(token):
        """Decodes the access token from the Authorization header."""
        try:
            # try to decode the token using our SECRET variable
            payload = jwt.decode(token, current_app.config.get(&apos;SECRET&apos;))
            return payload[&apos;sub&apos;]
        except jwt.ExpiredSignatureError:
            # the token is expired, return an error string
            return "Expired token. Please login to get a new token"
        except jwt.InvalidTokenError:
            # the token is invalid, return an error string
            return "Invalid token. Please register or login"

The generate_token() takes in a user ID as an argument, uses jwt to create a token using the secret key, and makes it time-based by defining its expiration time. The token is valid for 5 minutes as specified in the timedelta. You can set it to your liking.

The decode_token() takes in a token as an argument and checks whether the token is valid. If it is, it returns the user ID as the payload. It returns an error messsage if the token is expired or invalid.

Don't forget to import jwt and the datetime above.

The Auth Blueprint

Our app is growing bigger. We'll have to organize it into components. Flask uses the concept of **Blueprints ** to make application components.

Blueprints are simply a set of operations that can be registered on a given app. Think of it as an extension of the app that can address a specific functionality.

We'll create an authentication blueprint.

This blueprint will focus on handling user registration and logins.

Inside our /app directory create a folder and call it auth.

Our auth folder should contain:

  • We imported Flask-Bcrypt extension to help us in hashing our passwords. You should never store passwords in plaintext.
  • We created a User model that represents the users table. It contains the email and password fields to capture the user's credentials.
  • Since a user can own many bucketlists, we defined a One-to-Many relationship between the two tables. We defined this relationship by adding the db.relationship() function on the User table (parent table)
  • We added a foreign key on the child table (Bucketlist) referencing the User table. The foreign key has some arguments. The cascade="all, delete-orphan" will delete all bucketlists when a referenced user is deleted.
  • We hash the password by using generate_password_hash(pasword). This will make our users password be secure from dictionary and brute force attacks.
  • We refactored the get_all() method to get all the bucketlists for a given user.

In our auth/__init__.py file, initialize a blueprint.

# auth/__init__.py

from flask import Blueprint

# This instance of a Blueprint that represents the authentication blueprint
auth_blueprint = Blueprint(&apos;auth&apos;, __name__)

from . import views

Then import the blueprint and register it at the bottom of the app/__init__.py, just before the return app line.

# app/__init__.py

# imports lie here

def create_app(config_name):
    #####################################################
    ### Existing code for intializing the app with its configurations  ###
    #####################################################

    @app.route(&apos;/bucketlists/&apos;, methods=[&apos;GET&apos;, &apos;PUT&apos;, &apos;DELETE&apos;])
    def bucketlist_manipulation(id, **kwargs):
           #########################################################
        ### Existing code for creating, updating and deleting a bucketlist #####
        #########################################################
        ...

    # import the authentication blueprint and register it on the app
    from .auth import auth_blueprint
    app.register_blueprint(auth_blueprint)

    return app
Test First, Implement Later

Testing should never be an afterthought. It should always come first.

We're going to add a new test file that will house all our tests for the authentication blueprint.

It'll test whether our API can handle user registration, user login and access-token generation.

In our tests directory, create a file naming it test_auth.py. Write the following code in it:

# /tests/test_auth.py

import unittest
import json
from app import create_app, db

class AuthTestCase(unittest.TestCase):
    """Test case for the authentication blueprint."""

    def setUp(self):
        """Set up test variables."""
        self.app = create_app(config_name="testing")
        # initialize the test client
        self.client = self.app.test_client
        # This is the user test json data with a predefined email and password
        self.user_data = {
            &apos;email&apos;: &apos;[email protected]&apos;,
            &apos;password&apos;: &apos;test_password&apos;
        }

        with self.app.app_context():
            # create all tables
            db.session.close()
            db.drop_all()
            db.create_all()

    def test_registration(self):
        """Test user registration works correcty."""
        res = self.client().post(&apos;/auth/register&apos;, data=self.user_data)
        # get the results returned in json format
        result = json.loads(res.data.decode())
        # assert that the request contains a success message and a 201 status code
        self.assertEqual(result[&apos;message&apos;], "You registered successfully.")
        self.assertEqual(res.status_code, 201)

    def test_already_registered_user(self):
        """Test that a user cannot be registered twice."""
        res = self.client().post(&apos;/auth/register&apos;, data=self.user_data)
        self.assertEqual(res.status_code, 201)
        second_res = self.client().post(&apos;/auth/register&apos;, data=self.user_data)
        self.assertEqual(second_res.status_code, 202)
        # get the results returned in json format
        result = json.loads(second_res.data.decode())
        self.assertEqual(
            result[&apos;message&apos;], "User already exists. Please login.")

We've initialized our test with a test client for making requests to our API and some test data.

The first test function test_registration() sends a post request to /auth/register and tests the response it gets. It ensures that the status code is 201, meaning we've successfully created a user.

The second test function tests whether the API can only register a user once. Having duplicates in the database is bad for business.

Now let's run the tests using python manage.py test. The tests should fail.

----------------------------------------------------------------------
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
User Registration View

The reason our tests fail is simply because we lack the functionality they need to test. Let's implement something that'll make these two tests to pass.

Open up the views.py file and add the following code:

# /app/auth/views.py

from . import auth_blueprint

from flask.views import MethodView
from flask import make_response, request, jsonify
from app.models import User

class RegistrationView(MethodView):
    """This class registers a new user."""

    def post(self):
        """Handle POST request for this view. Url ---> /auth/register"""

        # Query to see if the user already exists
        user = User.query.filter_by(email=request.data[&apos;email&apos;]).first()

        if not user:
            # There is no user so we&apos;ll try to register them
            try:
                post_data = request.data
                # Register the user
                email = post_data[&apos;email&apos;]
                password = post_data[&apos;password&apos;]
                user = User(email=email, password=password)
                user.save()

                response = {
                    &apos;message&apos;: &apos;You registered successfully. Please log in.&apos;
                }
                # return a response notifying the user that they registered successfully
                return make_response(jsonify(response)), 201
            except Exception as e:
                # An error occured, therefore return a string message containing the error
                response = {
                    &apos;message&apos;: str(e)
                }
                return make_response(jsonify(response)), 401
        else:
            # There is an existing user. We don&apos;t want to register users twice
            # Return a message to the user telling them that they they already exist
            response = {
                &apos;message&apos;: &apos;User already exists. Please login.&apos;
            }

            return make_response(jsonify(response)), 202

registration_view = RegistrationView.as_view(&apos;register_view&apos;)
# Define the rule for the registration url --->  /auth/register
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    &apos;/auth/register&apos;,
    view_func=registration_view,
    methods=[&apos;POST&apos;])

Here's what we have added:

  • We imported Flask-Bcrypt extension to help us in hashing our passwords. You should never store passwords in plaintext.
  • We created a User model that represents the users table. It contains the email and password fields to capture the user's credentials.
  • Since a user can own many bucketlists, we defined a One-to-Many relationship between the two tables. We defined this relationship by adding the db.relationship() function on the User table (parent table)
  • We added a foreign key on the child table (Bucketlist) referencing the User table. The foreign key has some arguments. The cascade="all, delete-orphan" will delete all bucketlists when a referenced user is deleted.
  • We hash the password by using generate_password_hash(pasword). This will make our users password be secure from dictionary and brute force attacks.
  • We refactored the get_all() method to get all the bucketlists for a given user.

Let's run our tests once more. Only the AuthTestCase tests should pass. The bucketlist tests still fail because we havent modified the init.py` code.

test_already_registered_user (test_auth.AuthTestCase)
Test that a user cannot be registered twice. ... ok
test_registration (test_auth.AuthTestCase)
Test user registration works correcty. ... ok

Bucketlist failed tests fall here
----------------------------------------------------------------------

(venv)
Using Postman [ /auth/register ]

We’ll test our registration functionality by making a request using Postman.

But before we make the requests, ensure the API is up and running.

(venv) $ python run.py development

 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 225-021-817

Now you can make a POST request to localhost:5000/auth/register. Specify an email and a password of your choice to represent the user we are registering. Click send.

User Login

A user will have to login to gain access to our API. Currently, we are lacking this login functionality. Let's start with some tests. We'll add two more tests at the bottom of our test_auth.py as follows:

# tests/test_auth.py
class AuthTestCase(unittest.TestCase):
    """Test case for the authentication blueprint."""

    def setUp(self):
        #### EXISTING CODE FOR SETUP LIES HERE ####

    def test_registration(self):
        #### EXISTING TEST CODE LIES HERE ####

    def test_already_registered_user(self):
        ### EXISTING TEST CODE LIES HERE #####

    def test_user_login(self):
        """Test registered user can login."""
        res = self.client().post(&apos;/auth/register&apos;, data=self.user_data)
        self.assertEqual(res.status_code, 201)
        login_res = self.client().post(&apos;/auth/login&apos;, data=self.user_data)

        # get the results in json format
        result = json.loads(login_res.data.decode())
        # Test that the response contains success message
        self.assertEqual(result[&apos;message&apos;], "You logged in successfully.")
        # Assert that the status code is equal to 200
        self.assertEqual(login_res.status_code, 200)
        self.assertTrue(result[&apos;access_token&apos;])

    def test_non_registered_user_login(self):
        """Test non registered users cannot login."""
        # define a dictionary to represent an unregistered user
        not_a_user = {
            &apos;email&apos;: &apos;[email protected]&apos;,
            &apos;password&apos;: &apos;nope&apos;
        }
        # send a POST request to /auth/login with the data above
        res = self.client().post(&apos;/auth/login&apos;, data=not_a_user)
        # get the result in json
        result = json.loads(res.data.decode())

        # assert that this response must contain an error message 
        # and an error status code 401(Unauthorized)
        self.assertEqual(res.status_code, 401)
        self.assertEqual(
            result[&apos;message&apos;], "Invalid email or password, Please try again")

The test_user_login() function tests whether our API can successfully login a registered user. It also tests for the access token.

The other test function test_non_registered_user_login() tests whether our API can restrict signing in to only registered users.

Login View

Again, we'll make the tests pass by implementing its functionality. Let's create the login view.

from . import auth_blueprint

from flask.views import MethodView
from flask import Blueprint, make_response, request, jsonify
from app.models import User

class RegistrationView(MethodView):
    """This class-based view registers a new user."""
    #### EXISTING REGISTRATION CODE HERE ####
    ##########################################

class LoginView(MethodView):
    """This class-based view handles user login and access token generation."""

    def post(self):
        """Handle POST request for this view. Url ---> /auth/login"""
        try:
            # Get the user object using their email (unique to every user)
            user = User.query.filter_by(email=request.data[&apos;email&apos;]).first()

            # Try to authenticate the found user using their password
            if user and user.password_is_valid(request.data[&apos;password&apos;]):
                # Generate the access token. This will be used as the authorization header
                access_token = user.generate_token(user.id)
                if access_token:
                    response = {
                        &apos;message&apos;: &apos;You logged in successfully.&apos;,
                        &apos;access_token&apos;: access_token.decode()
                    }
                    return make_response(jsonify(response)), 200
            else:
                # User does not exist. Therefore, we return an error message
                response = {
                    &apos;message&apos;: &apos;Invalid email or password, Please try again&apos;
                }
                return make_response(jsonify(response)), 401

        except Exception as e:
            # Create a response containing an string error message
            response = {
                &apos;message&apos;: str(e)
            }
            # Return a server error using the HTTP Error Code 500 (Internal Server Error)
            return make_response(jsonify(response)), 500

# Define the API resource
registration_view = RegistrationView.as_view(&apos;registration_view&apos;)
login_view = LoginView.as_view(&apos;login_view&apos;)

# Define the rule for the registration url --->  /auth/register
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    &apos;/auth/register&apos;,
    view_func=registration_view,
    methods=[&apos;POST&apos;])

# Define the rule for the registration url --->  /auth/login
# Then add the rule to the blueprint
auth_blueprint.add_url_rule(
    &apos;/auth/login&apos;,
    view_func=login_view,
    methods=[&apos;POST&apos;]
)

Here, we've defined a class-based view just like we did on the registration section.

It dispatches the POST request to the post() method as well. This is to capture the user credentials(email, password) when they login. It checks whether the password given is valid, generates an access token for the user and returns a response containing the token.

We've also handled exceptions gracefully so that if one occurs, our API will continue running and won't crush.

Finally, we defined a url for the login route.

Logging in on Postman [ /auth/login ]

Make a POST request. Input the email and password we specified for the user during registration. Click send. You should get an access token in the JSON response.

Running the tests

If you run the tests, you will notice that the login tests pass, but the bucketlist one still fail. It's time to refactor this tests.

Refactor Bucketlist Tests

First, we'll create two helper functions for registering and signing in our test user.

# tests/test_bucketlist.py
class BucketlistTestCase(unittest.TestCase):
    """This class represents the bucketlist test case"""

    def setUp(self):
        """Set up test variables."""
     #### SETUP VARIABLES ARE HERE #####
     ####################################

    def register_user(self, email="[email protected]", password="test1234"):
        """This helper method helps register a test user."""
        user_data = {
            &apos;email&apos;: email,
            &apos;password&apos;: password
        }
        return self.client().post(&apos;/auth/register&apos;, data=user_data)

    def login_user(self, email="[email protected]", password="test1234"):
        """This helper method helps log in a test user."""
        user_data = {
            &apos;email&apos;: email,
            &apos;password&apos;: password
        }
        return self.client().post(&apos;/auth/login&apos;, data=user_data)

    ############################################
    ##### ALL OUR TESTS METHODS LIE HERE #######

# Make the tests conveniently executable
if __name__ == "__main__":
    unittest.main()

We do this so that when we want to register or login a test user (which is in all the tests), we don't have to repeat ourselves. We'll simply call the function and we are set.

Next, we'll define a way to get the access token and add it to the Authorization header in all our client requests. Here's a code snippet of the how were going to do it.


    def test_bucketlist_creation(self):
        """Test the API can create a bucketlist (POST request)"""
        # register a test user, then log them in
        self.register_user():
        result = self.login_user()
        # obtain the access token
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        # ensure the request has an authorization header set with the access token in it
        res = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)

We can now go ahead and refactor the whole test_bucketlist.py file. After refactoring all our request, we should have something like this:

import unittest
import os
import json
from app import create_app, db

class BucketlistTestCase(unittest.TestCase):
    """This class represents the bucketlist test case"""

    def setUp(self):
        """Define test variables and initialize app."""
        self.app = create_app(config_name="testing")
        self.client = self.app.test_client
        self.bucketlist = {&apos;name&apos;: &apos;Go to Borabora for vacay&apos;}

        # binds the app to the current context
        with self.app.app_context():
            # create all tables
            db.session.close()
            db.drop_all()
            db.create_all()

    def register_user(self, email="[email protected]", password="test1234"):
        user_data = {
            &apos;email&apos;: email,
            &apos;password&apos;: password
        }
        return self.client().post(&apos;/auth/register&apos;, data=user_data)

    def login_user(self, email="[email protected]", password="test1234"):
        user_data = {
            &apos;email&apos;: email,
            &apos;password&apos;: password
        }
        return self.client().post(&apos;/auth/login&apos;, data=user_data)

    def test_bucketlist_creation(self):
        """Test API can create a bucketlist (POST request)"""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        # create a bucketlist by making a POST request
        res = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)
        self.assertEqual(res.status_code, 201)
        self.assertIn(&apos;Go to Borabora&apos;, str(res.data))

    def test_api_can_get_all_bucketlists(self):
        """Test API can get a bucketlist (GET request)."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        # create a bucketlist by making a POST request
        res = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)
        self.assertEqual(res.status_code, 201)

        # get all the bucketlists that belong to the test user by making a GET request
        res = self.client().get(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
        )
        self.assertEqual(res.status_code, 200)
        self.assertIn(&apos;Go to Borabora&apos;, str(res.data))

    def test_api_can_get_bucketlist_by_id(self):
        """Test API can get a single bucketlist by using it&apos;s id."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        rv = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data=self.bucketlist)

        # assert that the bucketlist is created 
        self.assertEqual(rv.status_code, 201)
        # get the response data in json format
        results = json.loads(rv.data.decode())

        result = self.client().get(
            &apos;/bucketlists/{}&apos;.format(results[&apos;id&apos;]),
            headers=dict(Authorization="Bearer " + access_token))
        # assert that the bucketlist is actually returned given its ID
        self.assertEqual(result.status_code, 200)
        self.assertIn(&apos;Go to Borabora&apos;, str(result.data))

    def test_bucketlist_can_be_edited(self):
        """Test API can edit an existing bucketlist. (PUT request)"""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        # first, we create a bucketlist by making a POST request
        rv = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data={&apos;name&apos;: &apos;Eat, pray and love&apos;})
        self.assertEqual(rv.status_code, 201)
        # get the json with the bucketlist
        results = json.loads(rv.data.decode())

        # then, we edit the created bucketlist by making a PUT request
        rv = self.client().put(
            &apos;/bucketlists/{}&apos;.format(results[&apos;id&apos;]),
            headers=dict(Authorization="Bearer " + access_token),
            data={
                "name": "Dont just eat, but also pray and love :-)"
            })
        self.assertEqual(rv.status_code, 200)

        # finally, we get the edited bucketlist to see if it is actually edited.
        results = self.client().get(
            &apos;/bucketlists/{}&apos;.format(results[&apos;id&apos;]),
            headers=dict(Authorization="Bearer " + access_token))
        self.assertIn(&apos;Dont just eat&apos;, str(results.data))

    def test_bucketlist_deletion(self):
        """Test API can delete an existing bucketlist. (DELETE request)."""
        self.register_user()
        result = self.login_user()
        access_token = json.loads(result.data.decode())[&apos;access_token&apos;]

        rv = self.client().post(
            &apos;/bucketlists/&apos;,
            headers=dict(Authorization="Bearer " + access_token),
            data={&apos;name&apos;: &apos;Eat, pray and love&apos;})
        self.assertEqual(rv.status_code, 201)
        # get the bucketlist in json
        results = json.loads(rv.data.decode())

        # delete the bucketlist we just created
        res = self.client().delete(
            &apos;/bucketlists/{}&apos;.format(results[&apos;id&apos;]),
            headers=dict(Authorization="Bearer " + access_token),)
        self.assertEqual(res.status_code, 200)

        # Test to see if it exists, should return a 404
        result = self.client().get(
            &apos;/bucketlists/1&apos;,
            headers=dict(Authorization="Bearer " + access_token))
        self.assertEqual(result.status_code, 404)

# Make the tests conveniently executable
if __name__ == "__main__":
    unittest.main()
Refactor GET(all) and POST functionality

We'll refactor the methods that handle the HTTP requests for bucketlist creation and getting all the bucketlists. Open up /app/init.py file and edit as follows:

# /app/__init__.py

## imports ##
from flask import request, jsonify, abort, make_response

def create_app(config_name):
    from models import Bucketlist, User

    ###########################################
    ### EXISTING APP CONFIG CODE LIES HERE ###
    ###########################################

    @app.route(&apos;/bucketlists/&apos;, methods=[&apos;POST&apos;, &apos;GET&apos;])
    def bucketlists():
        # Get the access token from the header
        auth_header = request.headers.get(&apos;Authorization&apos;)
        access_token = auth_header.split(" ")[1]

        if access_token:
         # Attempt to decode the token and get the User ID
            user_id = User.decode_token(access_token)
            if not isinstance(user_id, str):
                # Go ahead and handle the request, the user is authenticated

                if request.method == "POST":
                    name = str(request.data.get(&apos;name&apos;, &apos;&apos;))
                    if name:
                        bucketlist = Bucketlist(name=name, created_by=user_id)
                        bucketlist.save()
                        response = jsonify({
                            &apos;id&apos;: bucketlist.id,
                            &apos;name&apos;: bucketlist.name,
                            &apos;date_created&apos;: bucketlist.date_created,
                            &apos;date_modified&apos;: bucketlist.date_modified,
                            &apos;created_by&apos;: user_id
                        })

                        return make_response(response), 201

                else:
                    # GET all the bucketlists created by this user
                    bucketlists = Bucketlist.query.filter_by(created_by=user_id)
                    results = []

                    for bucketlist in bucketlists:
                        obj = {
                            &apos;id&apos;: bucketlist.id,
                            &apos;name&apos;: bucketlist.name,
                            &apos;date_created&apos;: bucketlist.date_created,
                            &apos;date_modified&apos;: bucketlist.date_modified,
                            &apos;created_by&apos;: bucketlist.created_by
                        }
                        results.append(obj)

                    return make_response(jsonify(results)), 200
            else:
                # user is not legit, so the payload is an error message
                message = user_id
                response = {
                    &apos;message&apos;: message
                }
                return make_response(jsonify(response)), 401

We first added two imports: the User model and the make_response from Flask.

In the bucketlist function, we check for the authorization header from the request and extract the access token. Then, we decoded the token using User.decode_token(token) to give us the payload. The payload is expected to be a user ID if the token is valid and not expired. If the token is not valid or expired, the payload will be an error message as a string.

Test it on Postman Create a bucketlist or two

Copy the token and paste it to the header section, creating an Authorization header. Don't forget to put the word Bearer before the token with a space separating them like this:

Authorization: "Bearer dfg32r22349r40eiwoijr232394029wfopi23r2.2342..."

Make a POST request to localhost:5000/bucketlists/, specifying the name of the bucketlist. Click send.

Get all bucketlists for a given user

Ensure you've set the Authorization header just as we did for the POST request.

Make a GET request to localhost:5000/bucketlists/ and retrieve all the bucketlists our user just created.

Finally, Refactor GET(one), PUT and DELETE functionality

We'll refactor the PUT and DELETE functionality the same way we tackled the GET and POST.

# /app/__init__.py

## imports ##
from flask import request, jsonify, abort, make_response

def create_app(config_name):
    from models import Bucketlist, User

    ############################################################
    ### Existing code for initializing the app with its configurations lies here ###
    ############################################################

    @app.route(&apos;/bucketlists/&apos;, methods=[&apos;POST&apos;, &apos;GET&apos;])
    def bucketlists():
        #### CODE FOR  GET and POST LIES HERE#####
        ###############################

    @app.route(&apos;/bucketlists/&apos;, methods=[&apos;GET&apos;, &apos;PUT&apos;, &apos;DELETE&apos;])
    def bucketlist_manipulation(id, **kwargs):
        # get the access token from the authorization header
        auth_header = request.headers.get(&apos;Authorization&apos;)
        access_token = auth_header.split(" ")[1]

        if access_token:
            # Get the user id related to this access token
            user_id = User.decode_token(access_token)

            if not isinstance(user_id, str):
                # If the id is not a string(error), we have a user id
                # Get the bucketlist with the id specified from the URL ()
                bucketlist = Bucketlist.query.filter_by(id=id).first()
                if not bucketlist:
                    # There is no bucketlist with this ID for this User, so
                    # Raise an HTTPException with a 404 not found status code
                    abort(404)

                if request.method == "DELETE":
                    # delete the bucketlist using our delete method
                    bucketlist.delete()
                    return {
                        "message": "bucketlist {} deleted".format(bucketlist.id)
                    }, 200

                elif request.method == &apos;PUT&apos;:
                    # Obtain the new name of the bucketlist from the request data
                    name = str(request.data.get(&apos;name&apos;, &apos;&apos;))

                    bucketlist.name = name
                    bucketlist.save()

                    response = {
                        &apos;id&apos;: bucketlist.id,
                        &apos;name&apos;: bucketlist.name,
                        &apos;date_created&apos;: bucketlist.date_created,
                        &apos;date_modified&apos;: bucketlist.date_modified,
                        &apos;created_by&apos;: bucketlist.created_by
                    }
                    return make_response(jsonify(response)), 200
                else:
                    # Handle GET request, sending back the bucketlist to the user
                    response = {
                        &apos;id&apos;: bucketlist.id,
                        &apos;name&apos;: bucketlist.name,
                        &apos;date_created&apos;: bucketlist.date_created,
                        &apos;date_modified&apos;: bucketlist.date_modified,
                        &apos;created_by&apos;: bucketlist.created_by
                    }
                    return make_response(jsonify(response)), 200
            else:
                # user is not legit, so the payload is an error message
                message = user_id
                response = {
                    &apos;message&apos;: message
                }
                # return an error response, telling the user he is Unauthorized
                return make_response(jsonify(response)), 401

    # import the authentication blueprint and register it on the app
    from .auth import auth_blueprint
    app.register_blueprint(auth_blueprint)

    return app

Running python manage.py test should now yield passing tests.

test_already_registered_user (test_auth.AuthTestCase)
Test that a user cannot be registered twice. ... ok
test_non_registered_user_login (test_auth.AuthTestCase)
Test non registered users cannot login. ... ok
test_registration (test_auth.AuthTestCase)
Test user registration works correcty. ... ok
test_user_login (test_auth.AuthTestCase)
Test registered user can login. ... ok
test_api_can_get_all_bucketlists (test_bucketlist.BucketlistTestCase)
Test API can get a bucketlist (GET request). ... ok
test_api_can_get_bucketlist_by_id (test_bucketlist.BucketlistTestCase)
Test API can get a single bucketlist by using it&apos;s id. ... ok
test_bucketlist_can_be_edited (test_bucketlist.BucketlistTestCase)
Test API can edit an existing bucketlist. (PUT request) ... ok
test_bucketlist_creation (test_bucketlist.BucketlistTestCase)
Test API can create a bucketlist (POST request) ... ok
test_bucketlist_deletion (test_bucketlist.BucketlistTestCase)
Test API can delete an existing bucketlist. (DELETE request). ... ok

----------------------------------------------------------------------
Ran 9 tests in 1.579s

OK
(venv)

Now let's test to see if it works on Postman.

Fire up the API using python run.py development

Make a GET request for a single bucketlist to localhost:5000/bucketlists/2

Feel free to play around with the PUT and DELETE functionality.

Conclusion

We've covered quite a lot on securing our API. We went through defining a user model and integrating users into our API. We also covered token-based authentication and used an authentication blueprint to implement it.

Even though our main focus is to write the code, we should not let testing be an afterthought.

For us to improve on code quality, there has to be tests. Testing is the secret to increasing the agility of your product development. In everything project you do, put TTD first.

How to build REST APIs with Python, Flask, Connexion, and SQLAlchemy

How to build REST APIs with Python, Flask, Connexion, and SQLAlchemy

The goal of this article is to show you how to use Python 3, Flask, SQLAlchemy, and Connexion to build useful REST APIs that can include input and output validation, and provide Swagger documentation as a bonus.

If you’re writing a web application, then you’re probably thinking about making HTTP calls to your server to get data to populate the dynamic parts of your application.

The goal of this article is to show you how to use Python 3, Flask, SQLAlchemy, and Connexion to build useful REST APIs that can include input and output validation, and provide Swagger documentation as a bonus. Also included is a simple but useful single page web application that demonstrates using the API with JavaScript and updating the DOM with it.

The REST API you’ll be building will serve a simple people data structure where the people are keyed to the last name, and any updates are marked with a new timestamp.

This data could be represented in a database, saved in a file, or be accessible through some network protocol, but for us an in-memory data structure works fine. One of the purposes of an API is to decouple the data from the application that uses it, hiding the data implementation details.

How to build REST APIs with Python, Flask, SQLAlchemy, and Connexion - Part 1

In Part 1 of this series, you'll use Flask and Connexion to create a REST API providing CRUD operations to a simple in-memory structure called PEOPLE. That worked to demonstrate how the Connexion module helps you build a nice REST API along with interactive documentation.

Table of Contents

  • What REST Is
  • What REST Is Not
  • The People REST API
  • Getting Started
  • Using Connexion to Add a REST API Endpoint
  • Adding Connexion to the Server
    • The Swagger Configuration File
  • Handler for People Endpoint
    • The Swagger UI
  • Building Out the Complete API
    • The Swagger UI Completed
  • Demonstration Single-Page Application
    • Static Files
    • JavaScript File
    • Demo Application
  • Example Code
  • Conclusion

What REST Is

Before you get started creating an API server, I want to talk about REST a little bit. There’s lots of information about REST available through some quick Google searches, and my intent is not to duplicate that here.

I just want to share how I view REST and use it to build APIs. I see REST as a set of conventions taking advantage of the HTTP protocol to provide CRUD (Create, Read, Update, and Delete) behavior on things and collections of those things. The CRUD behavior maps nicely to the HTTP protocol method verbs like this:

You can perform these actions on a thing or resource. It’s useful to think about a resource as something a noun can be applied to: a person, an order for something, a person’s address. This idea of a resource lines up nicely with a URL (Unique Resource Locator).

A URL should identify a unique resource on the web, something that will work with the same thing over and over again for the same URL. With these two ideas, we have a way to take common application actions on something uniquely identified on the web. This is a very useful way of working.

This idea goes a long way towards creating clean APIs and performing the actions many applications want from the API.

What REST Is Not

Because REST is useful and helps map out how we might want to interact with a web based API, it’s sometimes used for things that don’t really fit well. There are lots of cases where what we want to do is perform some work or take an action directly. An example might be performing a string substitution, which granted is a silly thing to make an API for, but let’s go with it for now. Here’s a URL that might be constructed to provide this:

/api/substituteString/<string>/<search_string>/<sub_string>

Here, string is the string to make the substitution in, search_string is the string to replace, and sub_string is the string to replace search_string with. This can certainly be tied to some code in the server that does the intended work. But this has some problems in REST terms.

One problem that comes to mind is that this URL doesn’t point to a unique resource, so what it returns is entirely dependent on the path variable sections. Also, there is no concept of CRUD against this URL. It really only needs one HTTP method, and the method name conveys nothing about the action to take.

As an API, it’s not great either. The meaning of the path variables is dependent on their position in the URL. This could be addressed by changing the URL to use a query string instead, like this:

/api/substituteString?string=<string>&search_string=<search_string>&sub_string=<sub_string>

But the URL portion /api/substituteString isn’t a thing (noun) or collection of things at all: it’s an action (verb).

This doesn’t fit well in the REST conventions, and trying to force it to do so makes for a poor API. What the above really represents is an RPC, or Remote Procedure Call. Thinking about this as an RPC makes much more sense.

The beauty is that both REST and RPC conventions can coexist in an API without any problems! By keeping in mind the intention of each, you can use both to compliment each other in your API design. There are many situations where it would be useful to perform CRUD operations on something. There are also many situations where it would be useful to take an action with a thing (as a parameter) but not necessarily affect the thing itself.

The People REST API

For our example program, you’re going to create a REST API providing access to a collection of people with CRUD access to an individual person within that collection. Here’s the API design for the people collection:

Getting Started

First, you’ll create a simple web server using the Flask Micro Framework. To get started, create a directory where you can create the code. You should also work in a virtualenv so you can install modules later on, which you’ll need to do. In addition, create a templates directory.

The Python code below gets a very basic web server up and running, and responding with Hello World for a request for the home page:

from flask import (
    Flask,
    render_template
)

# Create the application instance
app = Flask(__name__, template_folder="templates")

# Create a URL route in our application for "/"
@app.route('/')
def home():
    """
    This function just responds to the browser ULR
    localhost:5000/

    :return:        the rendered template 'home.html'
    """
    return render_template('home.html')

# If we're running in stand alone mode, run the application
if __name__ == '__main__':
    app.run(debug=True)

You should also create a home.html in the templates folder, as this is what will be served to a browser when navigating to the URL '/'. Here is the contents of the home.html file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Application Home Page</title>
</head>
<body>
    <h2>
        Hello World!
    </h2>
</body>
</html>

You’ll notice the HTML file is named home.html rather than index.html. This is intentional because having an index.html file in the templates directory causes problems once you import the Connexion module in your program.

You can run your application with this command line in the directory containing the server.py file with the Python VirtualEnv active:

python server.py

When you run this application, a web server will start on port 5000, which is the default port used by Flask. If you open a browser and navigate to localhost:5000, you should see Hello World! displayed. Right now, this is useful to see the web server is running. You’ll extend the home.html file later to become a full single page web application using the REST API you’re developing.

In your Python program, you’ve imported the Flask module, giving the application access to the Flask functionality. You then created a Flask application instance, the app variable. Next, you connected the URL route '/' to the home() function by decorating it with @app.route('/'). This function calls the Flask render_template() function to get the home.html file from the templates directory and return it to the browser.

Using Connexion to Add a REST API Endpoint

Now that you’ve got a working web server, let’s add a REST API endpoint. To do this, you’ll use the Connexion module, which is installed using pip:

$ pip install connexion

This makes the Connexion module available to your program. The Connexion module allows a Python program to use the Swagger specification. This provides a lot of functionality: validation of input and output data to and from your API, an easy way to configure the API URL endpoints and the parameters expected, and a really nice UI interface to work with the created API and explore it.

All of this can happen when you create a configuration file your application can access. The Swagger site even provides an online configuration editor tool to help create and/or syntax check the configuration file you will create.

Adding Connexion to the Server

There are two parts to adding a REST API URL endpoint to your application with Connexion. You’ll add Connexion to the server and create a configuration file it will use. Modify your Python program like this to add Connexion to the server:

from flask import render_template
import connexion

# Create the application instance
app = connexion.App(__name__, specification_dir='./')

# Read the swagger.yml file to configure the endpoints
app.add_api('swagger.yml')

# Create a URL route in our application for "/"
@app.route('/')
def home():
    """
    This function just responds to the browser ULR
    localhost:5000/
    :return:        the rendered template 'home.html'
    """
    return render_template('home.html')

# If we're running in stand alone mode, run the application
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

You’ve added a couple of things to incorporate Connexion into the server. The import connexion statement adds the module to the program. The next step is creating the application instance using Connexion rather than Flask. Internally, the Flask app is still created, but it now has additional functionality added to it.

Part of the app instance creation includes the parameter specification_dir. This informs Connexion what directory to look in for its configuration file, in this case our current directory. Right after this, you’ve added the line:

app.add_api('swagger.yml')

This tells the app instance to read the file swagger.yml from the specification directory and configure the system to provide the Connexion functionality.

The Swagger Configuration File

The file swagger.yml is a YAML or JSON file containing all of the information necessary to configure your server to provide input parameter validation, output response data validation, URL endpoint definition, and the Swagger UI. Here is the swagger.yml file defining the GET /api/people endpoint your REST API will provide:

swagger: "2.0"
info:
  description: This is the swagger file that goes with our server code
  version: "1.0.0"
  title: Swagger REST Article
consumes:
  - "application/json"
produces:
  - "application/json"

basePath: "/api"

# Paths supported by the server application
paths:
  /people:
    get:
      operationId: "people.read"
      tags:
        - "People"
      summary: "The people data structure supported by the server application"
      description: "Read the list of people"
      responses:
        200:
          description: "Successful read people list operation"
          schema:
            type: "array"
            items:
              properties:
                fname:
                  type: "string"
                lname:
                  type: "string"
                timestamp:
                  type: "string"

This file is organized in a hierarchical manner: the indentation levels represent a level of ownership, or scope.

For example, paths defines the beginning of where all the API URL endpoints are defined. The /people value indented under that defines the start of where all the /api/people URL endpoints will be defined. The get: indented under that defines the section of definitions associated with an HTTP GET request to the /api/people URL endpoint. This goes on for the entire configuration.

Here are the meanings of the fields in this section of the swagger.yml file:

This section is part of the global configuration information:

  1. swagger: tells Connexion what version of the Swagger API is being used
  2. info: begins a new ‘scope’ of information about the API being built
  3. description: a user defined description of what the API provides or is about. This is in the Connexion generated UI system
  4. version: a user defined version value for the API
  5. title: a user defined title included in the Connexion generated UI system
  6. consumes: tells Connexion what MIME type is expected by the API.
  7. produces: tells Connexion what content type is expected by the caller of the API.
  8. basePath: "/api" defines the root of the API, kind of like a namespace the REST API will come from.

This section begins the configuration of the API URL endpoint paths:

  1. paths: defines the section of the configuration containing all of the API REST endpoints.
  2. /people: defines one path of your URL endpoint.
  3. get: defines the HTTP method this URL endpoint will respond to. Together with the previous definitions, this creates the GET /api/people URL endpoint.

This section begins the configuration of the single /api/people URL endpoint:

  1. operationId: "people.read" defines the Python import path/function that will respond to an HTTP GET /api/people request. The "people.read" portion can go as deep as you need to in order to connect a function to the HTTP request. Something like "<package_name>.<package_name>.<package_name>.<function_name>" would work just as well. You’ll create this shortly.
  2. tags: defines a grouping for the UI interface. All the CRUD methods you’ll define for the people endpoint will share this tag definition.
  3. summary defines the UI interface display text for this endpoint.
  4. description: defines what the UI interface will display for implementation notes.

This section defines the section of the configuration of a successful response from the URL endpoint:

  1. response: defines the beginning of the expected response section.
  2. 200: defines the section for a successful response, HTTP status code 200.
  3. description: defines the UI interface display text for a response of 200.
  4. schema: defines the response as a schema, or structure.
  5. type: defines the structure of the schema as an array.
  6. items: starts the definition of the items in the array.
  7. properties: defines the items in the array as objects having key/value pairs.
  8. fname: defines the first key of the object.
  9. type: defines the value associated with fname as a string.
  10. lname: defines the second key of the object.
  11. type: defines the value associated with lname as a string.
  12. timestamp: defines the third key of the object.
  13. type: defines the value associated with timestamp as a string.

Handler for People Endpoint

In the swagger.yml file, you configured Connexion with the operationId value to call the people module and the read function within the module when the API gets an HTTP request for GET /api/people. This means a people.py module must exist and contain a read() function. Here is the people.py module you will create:

from datetime import datetime

def get_timestamp():
    return datetime.now().strftime(("%Y-%m-%d %H:%M:%S"))

# Data to serve with our API
PEOPLE = {
    "Farrell": {
        "fname": "Doug",
        "lname": "Farrell",
        "timestamp": get_timestamp()
    },
    "Brockman": {
        "fname": "Kent",
        "lname": "Brockman",
        "timestamp": get_timestamp()
    },
    "Easter": {
        "fname": "Bunny",
        "lname": "Easter",
        "timestamp": get_timestamp()
    }
}

# Create a handler for our read (GET) people
def read():
    """
    This function responds to a request for /api/people
    with the complete lists of people

    :return:        sorted list of people
    """
    # Create the list of people from our data
    return [PEOPLE[key] for key in sorted(PEOPLE.keys())]

In this code, you’ve created a helper function called get_timestamp() that generates a string representation of the current timestamp. This is used to create your in-memory structure and modify the data when you start modifying it with the API.

You then created the PEOPLE dictionary data structure, which is a simple names database, keyed on the last name. This is a module variable, so its state persists between REST API calls. In a real application, the PEOPLE data would exist in a database, file, or network resource, something that persists the data beyond running/stopping the web application.

Then you created the read() function. This is called when an HTTP request to GET /api/people is received by the server. The return value of this function is converted to a JSON string (recall the produces: definition in the swagger.yml file). The read() function you created builds and returns a list of people sorted by last name.

Running your server code and navigating in a browser to localhost:5000/api/people will display the list of people on screen:

[
  {
    "fname": "Kent",
    "lname": "Brockman",
    "timestamp": "2018-05-10 18:12:42"
  },
  {
    "fname": "Bunny",
    "lname": "Easter",
    "timestamp": "2018-05-10 18:12:42"
  },
  {
    "fname": "Doug",
    "lname": "Farrell",
    "timestamp": "2018-05-10 18:12:42"
  }
]

Congratulations, you’ve created a nice API and are on your way to building out a complete one!

The Swagger UI

Now you have a simple web API running with a single URL endpoint. At this point, it would be reasonable to think, “configuring this with the swagger.yml file was cool and all, but what did it get me?”

You’d be right to think that. We haven’t taken advantage of the input or output validation. All that swagger.yml gave us was a definition for the code path connected to the URL endpoint. However, what you also get for the extra work is the creation of the Swagger UI for your API.

If you navigate to localhost:5000/api/ui, the system will bring up a page that looks something like this:

This is the initial Swagger interface and shows the list of URL endpoints supported at our localhost:5000/api endpoint. This is built automatically by Connexion when it parses the swagger.yml file.

If you click on the /people endpoint in the interface, the interface will expand to show a great deal more information about your API and should look something like this:

This displays the structure of the expected response, the content-type of that response, and the description text you entered about the endpoint in the swagger.yml file.

You can even try the endpoint out by clicking the Try It Out! button at the bottom of the screen. That will further expand the interface to look something like this:

This can be extremely useful when the API is complete as it gives you and your API users a way to explore and experiment with the API without having to write any code to do so.

Building an API this way is very useful to me at work. Not only is the Swagger UI useful as a way to experiment with the API and read the provided documentation, but it’s also dynamic. Any time the configuration file changes, the Swagger UI changes as well.

In addition, the configuration offers a nice, clean way to think about and create the API URL endpoints. I know from experience that APIs can develop in a sometimes random manner over time, making finding the code that supports the endpoints, and coordinating them, difficult at best.

By separating the code from the API URL endpoint configuration, we decouple one from the other. This alone has been very useful to me in my work building API systems supporting single page web applications.

Building Out the Complete API

Our original goal was to build out an API providing full CRUD access to our people structure. As you recall, the definition of our API looks like this:

To achieve this, you’ll extend both the swagger.yml and people.py files to fully support the API defined above. For the sake of brevity, only a link will be provided for both files:

[Remove ads](/account/join/)

The Swagger UI Completed

Once you’ve updated the swagger.yml and people.py files to complete the people API functionality, the Swagger UI system will update accordingly and look something like this:

This UI allows you to see all of the documentation you’ve included in the swagger.yml file and interact with all of the URL endpoints making up the CRUD functionality of the people interface.

Demonstration Single-Page Application

You’ve got a working REST API with a great Swagger UI documentation/interaction system. But what do you do with it now? The next step is to create a web application demonstrating the use of the API in a semi-practical manner.

You’ll create a web application that displays the people on screen as well as allows the user to create new people, update existing people, and delete people. This will all be handled by AJAX calls from JavaScript to the people API URL endpoints.

To begin, you need to extend the home.html file to look like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Application Home Page</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css">
    <link rel="stylesheet" href="static/css/home.css">
    <script
      src="http://code.jquery.com/jquery-3.3.1.min.js"
      integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
      crossorigin="anonymous">
    </script>
</head>
<body>
    <div class="container">
        <h1 class="banner">People Demo Application</h1>
        <div class="section editor">
            <label for="fname">First Name
                <input id="fname" type="text" />
            </label>
            <br />
            <label for="lname">Last Name
                <input id="lname" type="text" />
            </label>
            <br />
            <button id="create">Create</button>
            <button id="update">Update</button>
            <button id="delete">Delete</button>
            <button id="reset">Reset</button>
        </div>
        <div class="people">
            <table>
                <caption>People</caption>
                <thead>
                    <tr>
                        <th>First Name</th>
                        <th>Last Name</th>
                        <th>Update Time</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
        <div class="error">
        </div>
    </div>
</body>
<script src="static/js/home.js"></script>
</html>

The above HTML code extends the home.html file to pull in the external normalize.min.css file, which is a CSS reset file to normalize the formatting of elements across browsers.

It also pulls in the external jquery-3.3.1.min.js file to provide the jQuery functionality you’ll use to create the single-page application interactivity.

The HTML code above creates the static framework of the application. The dynamic parts appearing in the table structure will be added by JavaScript at load time and as the user interacts with the application.

Static Files

In the home.html file you’ve created, there are references to two static files: static/css/home.css and static/js/home.js. To add these, you’ll need to add the following directory structure to the application:

static/
│
├── css/
│   └── home.css
│
└── js/
    └── home.js

Because a directory named static will automatically be served by the Flask application, any files you place in the css and js folders will be available to the home.html file. For the sake of brevity, here are links to the home.css and home.js files:

JavaScript File

As was mentioned, the JavaScript file provides all the interaction with and updates to the web application. It does this by breaking up the necessary functionality into three parts by using the MVC (Model / View / Controller) design pattern.

Each object is created by a self-invoking function returning its own API for use by the other pieces. For instance, the Controller is passed the instances of the Model and View as parameters in its instantiation. It interacts with those objects through their exposed API methods.

The only other connection is between the Model and Controller by means of custom events on the AJAX method calls:

  1. The Model provides the connection to the people API. Its own API is what the Controller calls to interact with the server when a user interaction event requires it.
  2. The View provides the mechanism to update the web application DOM. Its own API is what the Controller calls to update the DOM when a user interaction event requires it.
  3. The Controller provides all the event handlers for user interaction with the web application. This allows it to make calls to the Model to make requests to the people API, and to the View to update the DOM with new information received from the people API.

It also handles the custom events generated by the asynchronous AJAX requests made by the Model to the people API.

Here is a diagram of the MVC structure used in the home.js file:

The idea is that the Controller has a strong link to both the Model and the View. The Model has a weak link (the custom events) to the Controller and no connection to the View at all. The weak link from the Model to the Controller reduces coupling and dependence, which is useful in this case.

Demo Application

When created, the web application will look like this in the browser:

The Create button allows the user to create a new person in the people structure on the server. When you enter a First and Last Name and hit Create, the Controller calls the Model to make a request to the POST /api/people URL endpoint. This will verify that the last name doesn’t already exist. If it doesn’t, it will create a new person in the people structure.

This generates a custom event in the Model that causes the Controller to call the Model again to request a GET /api/people, which will return the complete list of people sorted. The Controller then passes that onto the View to redraw the table of people.

Double clicking on any row in the table will populate the First and Last Name fields in the editor section of the application. At this point, the user can either update or delete the person.

To update successfully, the user must change something about the First Name. The Last Name must remain the same as it’s the lookup key for the person to update. When Update is clicked, the Controller calls the Model to make a request to the PUT /api/people/{lname} URL endpoint. This will verify that the Last Name does currently exist. If so, it will update that person in the people structure.

This generates a custom event in the Model that causes the Controller to call the Model again to request a GET /api/people, which will return the complete list of people sorted. The Controller then passes that onto the View to redraw the table of people.

To delete successfully, the user need only click Delete. When Delete is clicked, the Controller calls the Model to make a request to the DELETE /api/people/{lname} URL endpoint. This will verify that the last name does currently exist. If so, it will delete that person from the people structure.

This generates a custom event in the Model that causes the Controller to call the Model again to request a GET /api/people, which will return the complete list of people sorted. The Controller then passes that onto the View to redraw the table of people.

Try making intentional errors in the editor, like misspelling a Last Name, and see the errors generated by the API represented on the web application.

Example Code

All of the example code for this article is available here. There are four versions of the code, each in a version_# directory, where # ranges from 1 to 4. The four versions correspond to the article sections in this manner:

  1. version_1: This version contains the initial web server that serves up the home.html file.
  2. version_2: This version contains the web server with Connexion added and the first people API URL endpoint.
  3. version_3: This version contains the completed people API with all supported URL endpoints.
  4. version_4: This version contains the completed API and a single page web application to demonstrate it.

Conclusion

In this tutorial, you saw how relatively easy it is to create a comprehensive REST API with Python. With the Connexion module and some additional configuration work, a useful documentation and interactive system can be put in place, making your API a much more enjoyable experience for your users to interface with and understand.

Python REST APIs With Flask, Connexion, and SQLAlchemy – Part 2

In Part 2 of this series, you'll add the ability to save changes made through the REST API to a database using SQLAlchemy and learned how to serialize that data for the REST API using Marshmallow. Connecting the REST API to a database so that the application can make changes to existing data and create new data is great and makes the application much more useful and robust.

SQLAlchemy provides an Object Relational Model (ORM), which stores Python objects to a database representation of the object’s data. That can help you continue to think in a Pythonic way and not be concerned with how the object data will be represented in a database.

Marshmallow provides functionality to serialize and deserialize Python objects as they flow out of and into our JSON-based REST API. Marshmallow converts Python class instances to objects that can be converted to JSON.

Table of Contents

  • Who This Article Is For
  • Additional Dependencies
  • People Data
  • Database Interaction
    • Little Bobby Tables
    • Modeling Data With SQLAlchemy
    • Serializing/Deserializing Modeled Data
  • Create the Initialized Database
    • Config Module
    • Models Module
    • Creating the Database
  • Using the Database
    • Update the REST API
    • Update the REST API Handlers
    • Update the Swagger UI
    • Update the Web Application
  • Example Code
  • Conclusion

Who This Article Is For

If you enjoyed Part 1 of this series, this article expands your tool belt even further. You’ll be using SQLAlchemy to access a database in a more Pythonic way than straight SQL. You’ll also use Marshmallow to serialize and deserialize the data managed by the REST API. To do this, you’ll be making use of basic Object Oriented Programming features available in Python.

You’ll also be using SQLAlchemy to create a database as well as interact with it. This is necessary to get the REST API up and running with the PEOPLE data used in Part 1.

The web application presented in Part 1 will have its HTML and JavaScript files modified in minor ways in order to support the changes as well.

Additional Dependencies

Before you get started building this new functionality, you’ll need to update the virtualenv you created in order to run the Part 1 code, or create a new one for this project. The easiest way to do that after you have activated your virtualenv is to run this command:

$ pip install Flask-SQLAlchemy flask-marshmallow marshmallow-sqlalchemy marshmallow

This adds more functionality to your virtualenv:

  1. Flask-SQLAlchemy adds SQLAlchemy, along with some tie-ins to Flask, allowing programs to access databases.

  2. flask-marshmallow adds the Flask parts of Marshmallow, which lets programs convert Python objects to and from serializable structures.

  3. marshmallow-sqlalchemy adds some Marshmallow hooks into SQLAlchemy to allow programs to serialize and deserialize Python objects generated by SQLAlchemy.

  4. marshmallow adds the bulk of the Marshmallow functionality.

People Data

As mentioned above, the PEOPLE data structure in the previous article is an in-memory Python dictionary. In that dictionary, you used the person’s last name as the lookup key. The data structure looked like this in the code:

# Data to serve with our API
PEOPLE = {
    "Farrell": {
        "fname": "Doug",
        "lname": "Farrell",
        "timestamp": get_timestamp()
    },
    "Brockman": {
        "fname": "Kent",
        "lname": "Brockman",
        "timestamp": get_timestamp()
    },
    "Easter": {
        "fname": "Bunny",
        "lname": "Easter",
        "timestamp": get_timestamp()
    }
}

The modifications you’ll make to the program will move all the data to a database table. This means the data will be saved to your disk and will exist between runs of the server.py program.

Because the last name was the dictionary key, the code restricted changing a person’s last name: only the first name could be changed. In addition, moving to a database will allow you to change the last name as it will no longer be used as the lookup key for a person.

Conceptually, a database table can be thought of as a two-dimensional array where the rows are records, and the columns are fields in those records.

Database tables usually have an auto-incrementing integer value as the lookup key to rows. This is called the primary key. Each record in the table will have a primary key whose value is unique across the entire table. Having a primary key independent of the data stored in the table frees you to modify any other field in the row.

Note:

The auto-incrementing primary key means that the database takes care of:

  • Incrementing the largest existing primary key field every time a new record is inserted in the table
  • Using that value as the primary key for the newly inserted data

This guarantees a unique primary key as the table grows.

You’re going to follow a database convention of naming the table as singular, so the table will be called person. Translating our PEOPLE structure above into a database table named person gives you this:

Each column in the table has a field name as follows:

  • person_id: primary key field for each person
  • lname: last name of the person
  • fname: first name of the person
  • timestamp: timestamp associated with insert/update actions

Database Interaction

You’re going to use SQLite as the database engine to store the PEOPLE data. SQLite is the mostly widely distributed database in the world, and it comes with Python for free. It’s fast, performs all its work using files, and is suitable for a great many projects. It’s a complete RDBMS (Relational Database Management System) that includes SQL, the language of many database systems.

For the moment, imagine the person table already exists in a SQLite database. If you’ve had any experience with RDBMS, you’re probably aware of SQL, the Structured Query Language most RDBMSes use to interact with the database.

Unlike programming languages like Python, SQL doesn’t define how to get the data: it describes what data is desired, leaving the how up to the database engine.

A SQL query getting all of the data in our person table, sorted by last name, would look this this:

SELECT * FROM person ORDER BY 'lname';

This query tells the database engine to get all the fields from the person table and sort them in the default, ascending order using the lname field.

If you were to run this query against a SQLite database containing the person table, the results would be a set of records containing all the rows in the table, with each row containing the data from all the fields making up a row. Below is an example using the SQLite command line tool running the above query against the person database table:

sqlite> SELECT * FROM person ORDER BY lname;
2|Brockman|Kent|2018-08-08 21:16:01.888444
3|Easter|Bunny|2018-08-08 21:16:01.889060
1|Farrell|Doug|2018-08-08 21:16:01.886834

The output above is a list of all the rows in the person database table with pipe characters (‘|’) separating the fields in the row, which is done for display purposes by SQLite.

Python is completely capable of interfacing with many database engines and executing the SQL query above. The results would most likely be a list of tuples. The outer list contains all the records in the person table. Each individual inner tuple would contain all the data representing each field defined for a table row.

Getting data this way isn’t very Pythonic. The list of records is okay, but each individual record is just a tuple of data. It’s up to the program to know the index of each field in order to retrieve a particular field. The following Python code uses SQLite to demonstrate how to run the above query and display the data:

import sqlite3


conn = sqlite3.connect('people.db')

cur = conn.cursor()

cur.execute('SELECT * FROM person ORDER BY lname')

people = cur.fetchall()

for person in people:

    print(f'{person[2]} {person[1]}')

The program above does the following:

  • Line 1 imports the sqlite3 module.

  • Line 3 creates a connection to the database file.

  • Line 4 creates a cursor from the connection.

  • Line 5 uses the cursor to execute a SQL query expressed as a string.

  • Line 6 gets all the records returned by the SQL query and assigns them to the people variable.

  • Line 7 & 8 iterate over the people list variable and print out the first and last name of each person.

The people variable from Line 6 above would look like this in Python:

people = [
    (2, 'Brockman', 'Kent', '2018-08-08 21:16:01.888444'), 
    (3, 'Easter', 'Bunny', '2018-08-08 21:16:01.889060'), 
    (1, 'Farrell', 'Doug', '2018-08-08 21:16:01.886834')
]

The output of the program above looks like this:

Kent Brockman
Bunny Easter
Doug Farrell

In the above program, you have to know that a person’s first name is at index 2, and a person’s last name is at index 1. Worse, the internal structure of person must also be known whenever you pass the iteration variable person as a parameter to a function or method.

It would be much better if what you got back for person was a Python object, where each of the fields is an attribute of the object. This is one of the things SQLAlchemy does.

Little Bobby Tables

In the above program, the SQL statement is a simple string passed directly to the database to execute. In this case, that’s not a problem because the SQL is a string literal completely under the control of the program. However, the use case for your REST API will take user input from the web application and use it to create SQL queries. This can open your application to attack.

You’ll recall from Part 1 that the REST API to get a single person from the PEOPLE data looked like this:

GET /api/people/{lname}

This means your API is expecting a variable, lname, in the URL endpoint path, which it uses to find a single person. Modifying the Python SQLite code from above to do this would look something like this:

lname = 'Farrell'

cur.execute('SELECT * FROM person WHERE lname = \'{}\''.format(lname))

The above code snippet does the following:

  • Line 1 sets the lname variable to 'Farrell'. This would come from the REST API URL endpoint path.

  • Line 2 uses Python string formatting to create a SQL string and execute it.

To keep things simple, the above code sets the lname variable to a constant, but really it would come from the API URL endpoint path and could be anything supplied by the user. The SQL generated by the string formatting looks like this:

SELECT * FROM person WHERE lname = 'Farrell'

When this SQL is executed by the database, it searches the person table for a record where the last name is equal to 'Farrell'. This is what’s intended, but any program that accepts user input is also open to malicious users. In the program above, where the lname variable is set by user-supplied input, this opens your program to what’s called a SQL Injection Attack. This is what’s affectionately known as Little Bobby Tables:

Image: [xkcd.com](https://xkcd.com/327/)

For example, imagine a malicious user called your REST API in this way:

GET /api/people/Farrell');DROP TABLE person;

The REST API request above sets the lname variable to 'Farrell');DROP TABLE person;', which in the code above would generate this SQL statement:

SELECT * FROM person WHERE lname = 'Farrell');DROP TABLE person;

The above SQL statement is valid, and when executed by the database it will find one record where lname matches 'Farrell'. Then, it will find the SQL statement delimiter character ; and will go right ahead and drop the entire table. This would essentially wreck your application.

You can protect your program by sanitizing all data you get from users of your application. Sanitizing data in this context means having your program examine the user-supplied data and making sure it doesn’t contain anything dangerous to the program. This can be tricky to do right and would have to be done everywhere user data interacts with the database.

There’s another way that’s much easier: use SQLAlchemy. It will sanitize user data for you before creating SQL statements. It’s another big advantage and reason to use SQLAlchemy when working with databases.

Modeling Data With SQLAlchemy

SQLAlchemy is a big project and provides a lot of functionality to work with databases using Python. One of the things it provides is an ORM, or Object Relational Mapper, and this is what you’re going to use to create and work with the person database table. This allows you to map a row of fields from the database table to a Python object.

Object Oriented Programming allows you to connect data together with behavior, the functions that operate on that data. By creating SQLAlchemy classes, you’re able to connect the fields from the database table rows to behavior, allowing you to interact with the data. Here’s the SQLAlchemy class definition for the data in the person database table:

class Person(db.Model):
    __tablename__ = 'person'
    person_id = db.Column(db.Integer, 
                          primary_key=True)
    lname = db.Column(db.String)
    fname = db.Column(db.String)
    timestamp = db.Column(db.DateTime, 
                          default=datetime.utcnow, 
                          onupdate=datetime.utcnow)

The class Person inherits from db.Model, which you’ll get to when you start building the program code. For now, it means you’re inheriting from a base class called Model, providing attributes and functionality common to all classes derived from it.

The rest of the definitions are class-level attributes defined as follows:

  • __tablename__ = 'person' connects the class definition to the person database table.

  • person_id = db.Column(db.Integer, primary_key=True) creates a database column containing an integer acting as the primary key for the table. This also tells the database that person_id will be an autoincrementing Integer value.

  • lname = db.Column(db.String) creates the last name field, a database column containing a string value.

  • fname = db.Column(db.String) creates the first name field, a database column containing a string value.

  • timestamp = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) creates a timestamp field, a database column containing a date/time value. The default=datetime.utcnow parameter defaults the timestamp value to the current utcnow value when a record is created. The onupdate=datetime.utcnow parameter updates the timestamp with the current utcnow value when the record is updated.

Note: UTC Timestamps

You might be wondering why the timestamp in the above class defaults to and is updated by the datetime.utcnow() method, which returns a UTC, or Coordinated Universal Time. This is a way of standardizing your timestamp’s source.

The source, or zero time, is a line running north and south from the Earth’s north to south pole through the UK. This is the zero time zone from which all other time zones are offset. By using this as the zero time source, your timestamps are offsets from this standard reference point.

Should your application be accessed from different time zones, you have a way to perform date/time calculations. All you need is a UTC timestamp and the destination time zone.

If you were to use local time zones as your timestamp source, then you couldn’t perform date/time calculations without information about the local time zones offset from zero time. Without the timestamp source information, you couldn’t do any date/time comparisons or math at all.

Working with a timestamps based on UTC is a good standard to follow. Here’s a toolkit site to work with and better understand them.

Where are you heading with this Person class definition? The end goal is to be able to run a query using SQLAlchemy and get back a list of instances of the Person class. As an example, let’s look at the previous SQL statement:

SELECT * FROM people ORDER BY lname;

Show the same small example program from above, but now using SQLAlchemy:

from models import Person


people = Person.query.order_by(Person.lname).all()

for person in people:

    print(f'{person.fname} {person.lname}')

Ignoring line 1 for the moment, what you want is all the person records sorted in ascending order by the lname field. What you get back from the SQLAlchemy statements Person.query.order_by(Person.lname).all() is a list of Person objects for all records in the person database table in that order. In the above program, the people variable contains the list of Person objects.

The program iterates over the people variable, taking each person in turn and printing out the first and last name of the person from the database. Notice the program doesn’t have to use indexes to get the fname or lname values: it uses the attributes defined on the Person object.

Using SQLAlchemy allows you to think in terms of objects with behavior rather than raw SQL. This becomes even more beneficial when your database tables become larger and the interactions more complex.

Serializing/Deserializing Modeled Data

Working with SQLAlchemy modeled data inside your programs is very convenient. It is especially convenient in programs that manipulate the data, perhaps making calculations or using it to create presentations on screen. Your application is a REST API essentially providing CRUD operations on the data, and as such it doesn’t perform much data manipulation.

The REST API works with JSON data, and here you can run into an issue with the SQLAlchemy model. Because the data returned by SQLAlchemy are Python class instances, Connexion can’t serialize these class instances to JSON formatted data. Remember from Part 1 that Connexion is the tool you used to design and configure the REST API using a YAML file, and connect Python methods to it.

In this context, serializing means converting Python objects, which can contain other Python objects and complex data types, into simpler data structures that can be parsed into JSON datatypes, which are listed here:

  • string: a string type
  • number: numbers supported by Python (integers, floats, longs)
  • object: a JSON object, which is roughly equivalent to a Python dictionary
  • array: roughly equivalent to a Python List
  • boolean: represented in JSON as true or false, but in Python as True or False
  • null: essentially a None in Python

As an example, your Person class contains a timestamp, which is a Python DateTime. There is no date/time definition in JSON, so the timestamp has to be converted to a string in order to exist in a JSON structure.

Your Person class is simple enough so getting the data attributes from it and creating a dictionary manually to return from our REST URL endpoints wouldn’t be very hard. In a more complex application with many larger SQLAlchemy models, this wouldn’t be the case. A better solution is to use a module called Marshmallow to do the work for you.

Marshmallow helps you to create a PersonSchema class, which is like the SQLAlchemy Person class we created. Here however, instead of mapping database tables and field names to the class and its attributes, the PersonSchema class defines how the attributes of a class will be converted into JSON-friendly formats. Here’s the Marshmallow class definition for the data in our person table:

class PersonSchema(ma.ModelSchema):
    class Meta:
        model = Person
        sqla_session = db.session

The class PersonSchema inherits from ma.ModelSchema, which you’ll get to when you start building the program code. For now, this means PersonSchema is inheriting from a Marshmallow base class called ModelSchema, providing attributes and functionality common to all classes derived from it.

The rest of the definition is as follows:

  • class Meta defines a class named Meta within your class. The ModelSchema class that the PersonSchema class inherits from looks for this internal Meta class and uses it to find the SQLAlchemy model Person and the db.session. This is how Marshmallow finds attributes in the Person class and the type of those attributes so it knows how to serialize/deserialize them.

  • model tells the class what SQLAlchemy model to use to serialize/deserialize data to and from.

  • db.session tells the class what database session to use to introspect and determine attribute data types.

Where are you heading with this class definition? You want to be able to serialize an instance of a Person class into JSON data, and to deserialize JSON data and create a Person class instances from it.

Create the Initialized Database

SQLAlchemy handles many of the interactions specific to particular databases and lets you focus on the data models as well as how to use them.

Now that you’re actually going to create a database, as mentioned before, you’ll use SQLite. You’re doing this for a couple of reasons. It comes with Python and doesn’t have to be installed as a separate module. It saves all of the database information in a single file and is therefore easy to set up and use.

Installing a separate database server like MySQL or PostgreSQL would work fine but would require installing those systems and getting them up and running, which is beyond the scope of this article.

Because SQLAlchemy handles the database, in many ways it really doesn’t matter what the underlying database is.

You’re going to create a new utility program called build_database.py to create and initialize the SQLite people.db database file containing your person database table. Along the way, you’ll create two Python modules, config.py and models.py, which will be used by build_database.py and the modified server.py from Part 1.

Here’s where you can find the source code for the modules you’re about to create, which are introduced here:

  • config.py gets the necessary modules imported into the program and configured. This includes Flask, Connexion, SQLAlchemy, and Marshmallow. Because it will be used by both build_database.py and server.py, some parts of the configuation will only apply to the server.py application.

  • models.py is the module where you’ll create the Person SQLAlchemy and PersonSchema Marshmallow class definitions described above. This module is dependent on config.py for some of the objects created and configured there.

Config Module

The config.py module, as the name implies, is where all of the configuration information is created and initialized. We’re going to use this module for both our build_database.py program file and the soon to be updated server.py file from the Part 1 article. This means we’re going to configure Flask, Connexion, SQLAlchemy, and Marshmallow here.

Even though the build_database.py program doesn’t make use of Flask, Connexion, or Marshmallow, it does use SQLAlchemy to create our connection to the SQLite database. Here is the code for the config.py module:

import os

import connexion

from flask_sqlalchemy import SQLAlchemy

from flask_marshmallow import Marshmallow


basedir = os.path.abspath(os.path.dirname(__file__))


# Create the Connexion application instance

connex_app = connexion.App(__name__, specification_dir=basedir)


# Get the underlying Flask app instance

app = connex_app.app


# Configure the SQLAlchemy part of the app instance

app.config['SQLALCHEMY_ECHO'] = True

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////' + os.path.join(basedir, 'people.db')

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False


# Create the SQLAlchemy db instance

db = SQLAlchemy(app)


# Initialize Marshmallow

ma = Marshmallow(app)

Here’s what the above code is doing:

  • Lines 2 – 4 import Connexion as you did in the server.py program from Part 1. It also imports SQLAlchemy from the flask_sqlalchemy module. This gives your program database access. Lastly, it imports Marshmallow from the flask_marshamllow module.

  • Line 6 creates the variable basedir pointing to the directory the program is running in.

  • Line 9 uses the basedir variable to create the Connexion app instance and give it the path to the swagger.yml file.

  • Line 12 creates a variable app, which is the Flask instance initialized by Connexion.

  • Lines 15 uses the app variable to configure values used by SQLAlchemy. First it sets SQLALCHEMY_ECHO to True. This causes SQLAlchemy to echo SQL statements it executes to the console. This is very useful to debug problems when building database programs. Set this to False for production environments.

  • Line 16 sets SQLALCHEMY_DATABASE_URI to sqlite:////' + os.path.join(basedir, 'people.db'). This tells SQLAlchemy to use SQLite as the database, and a file named people.db in the current directory as the database file. Different database engines, like MySQL and PostgreSQL, will have different SQLALCHEMY_DATABASE_URI strings to configure them.

  • Line 17 sets SQLALCHEMY_TRACK_MODIFICATIONS to False, turning off the SQLAlchemy event system, which is on by default. The event system generates events useful in event-driven programs but adds significant overhead. Since you’re not creating an event-driven program, turn this feature off.

  • Line 19 creates the db variable by calling SQLAlchemy(app). This initializes SQLAlchemy by passing the app configuration information just set. The db variable is what’s imported into the build_database.py program to give it access to SQLAlchemy and the database. It will serve the same purpose in the server.py program and people.py module.

  • Line 23 creates the ma variable by calling Marshmallow(app). This initializes Marshmallow and allows it to introspect the SQLAlchemy components attached to the app. This is why Marshmallow is initialized after SQLAlchemy.

Models Module

The models.py module is created to provide the Person and PersonSchema classes exactly as described in the sections above about modeling and serializing the data. Here is the code for that module:

from datetime import datetime

from config import db, ma


class Person(db.Model):

    __tablename__ = 'person'

    person_id = db.Column(db.Integer, primary_key=True)

    lname = db.Column(db.String(32), index=True)

    fname = db.Column(db.String(32))

    timestamp = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)


class PersonSchema(ma.ModelSchema):

    class Meta:

        model = Person

        sqla_session = db.session    

Here’s what the above code is doing:

  • Line 1 imports the datatime object from the datetime module that comes with Python. This gives you a way to create a timestamp in the Person class.

  • Line 2 imports the db and ma instance variables defined in the config.py module. This gives the module access to SQLAlchemy attributes and methods attached to the db variable, and the Marshmallow attributes and methods attached to the ma variable.

  • Lines 4 – 9 define the Person class as discussed in the data modeling section above, but now you know where the db.Model that the class inherits from originates. This gives the Person class SQLAlchemy features, like a connection to the database and access to its tables.

  • Lines 11 – 14 define the PersonSchema class as was discussed in the data serialzation section above. This class inherits from ma.ModelSchema and gives the PersonSchema class Marshmallow features, like introspecting the Person class to help serialize/deserialize instances of that class.

Creating the Database

You’ve seen how database tables can be mapped to SQLAlchemy classes. Now use what you’ve learned to create the database and populate it with data. You’re going to build a small utility program to create and build the database with the People data. Here’s the build_database.py program:

import os

from config import db

from models import Person


# Data to initialize database with

PEOPLE = [

    {'fname': 'Doug', 'lname': 'Farrell'},

    {'fname': 'Kent', 'lname': 'Brockman'},

    {'fname': 'Bunny','lname': 'Easter'}

]


# Delete database file if it exists currently

if os.path.exists('people.db'):

    os.remove('people.db')


# Create the database

db.create_all()


# Iterate over the PEOPLE structure and populate the database

for person in PEOPLE:

    p = Person(lname=person['lname'], fname=person['fname'])

    db.session.add(p)


db.session.commit()

Here’s what the above code is doing:

  • Line 2 imports the db instance from the config.py module.

  • Line 3 imports the Person class definition from the models.py module.

  • Lines 6 – 10 create the PEOPLE data structure, which is a list of dictionaries containing your data. The structure has been condensed to save presentation space.

  • Lines 13 & 14 perform some simple housekeeping to delete the people.db file, if it exists. This file is where the SQLite database is maintained. If you ever have to re-initialize the database to get a clean start, this makes sure you’re starting from scratch when you build the database.

  • Line 17 creates the database with the db.create_all() call. This creates the database by using the db instance imported from the config module. The db instance is our connection to the database.

  • Lines 20 – 22 iterate over the PEOPLE list and use the dictionaries within to instantiate a Person class. After it is instantiated, you call the db.session.add(p) function. This uses the database connection instance db to access the session object. The session is what manages the database actions, which are recorded in the session. In this case, you are executing the add(p) method to add the new Person instance to the session object.

  • Line 24 calls db.session.commit() to actually save all the person objects created to the database.

Note: At Line 22, no data has been added to the database. Everything is being saved within the session object. Only when you execute the db.session.commit() call at Line 24 does the session interact with the database and commit the actions to it.

In SQLAlchemy, the session is an important object. It acts as the conduit between the database and the SQLAlchemy Python objects created in a program. The session helps maintain the consistency between data in the program and the same data as it exists in the database. It saves all database actions and will update the underlying database accordingly by both explicit and implicit actions taken by the program.

Now you’re ready to run the build_database.py program to create and initialize the new database. You do so with the following command, with your Python virtual environment active:

python build_database.py

When the program runs, it will print SQLAlchemy log messages to the console. These are the result of setting SQLALCHEMY_ECHO to True in the config.py file. Much of what’s being logged by SQLAlchemy is the SQL commands it’s generating to create and build the people.db SQLite database file. Here’s an example of what’s printed out when the program is run:

2018-09-11 22:20:29,951 INFO sqlalchemy.engine.base.Engine SELECT CAST('test plain returns' AS VARCHAR(60)) AS anon_1
2018-09-11 22:20:29,951 INFO sqlalchemy.engine.base.Engine ()
2018-09-11 22:20:29,952 INFO sqlalchemy.engine.base.Engine SELECT CAST('test unicode returns' AS VARCHAR(60)) AS anon_1
2018-09-11 22:20:29,952 INFO sqlalchemy.engine.base.Engine ()
2018-09-11 22:20:29,956 INFO sqlalchemy.engine.base.Engine PRAGMA table_info("person")
2018-09-11 22:20:29,956 INFO sqlalchemy.engine.base.Engine ()
2018-09-11 22:20:29,959 INFO sqlalchemy.engine.base.Engine 
CREATE TABLE person (
    person_id INTEGER NOT NULL, 
    lname VARCHAR, 
    fname VARCHAR, 
    timestamp DATETIME, 
    PRIMARY KEY (person_id)
)
2018-09-11 22:20:29,959 INFO sqlalchemy.engine.base.Engine ()
2018-09-11 22:20:29,975 INFO sqlalchemy.engine.base.Engine COMMIT
2018-09-11 22:20:29,980 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
2018-09-11 22:20:29,983 INFO sqlalchemy.engine.base.Engine INSERT INTO person (lname, fname, timestamp) VALUES (?, ?, ?)
2018-09-11 22:20:29,983 INFO sqlalchemy.engine.base.Engine ('Farrell', 'Doug', '2018-09-12 02:20:29.983143')
2018-09-11 22:20:29,984 INFO sqlalchemy.engine.base.Engine INSERT INTO person (lname, fname, timestamp) VALUES (?, ?, ?)
2018-09-11 22:20:29,985 INFO sqlalchemy.engine.base.Engine ('Brockman', 'Kent', '2018-09-12 02:20:29.984821')
2018-09-11 22:20:29,985 INFO sqlalchemy.engine.base.Engine INSERT INTO person (lname, fname, timestamp) VALUES (?, ?, ?)
2018-09-11 22:20:29,985 INFO sqlalchemy.engine.base.Engine ('Easter', 'Bunny', '2018-09-12 02:20:29.985462')
2018-09-11 22:20:29,986 INFO sqlalchemy.engine.base.Engine COMMIT

Using the Database

Once the database has been created, you can modify the existing code from Part 1 to make use of it. All of the modifications necessary are due to creating the person_id primary key value in our database as the unique identifier rather than the lname value.

Update the REST API

None of the changes are very dramatic, and you’ll start by re-defining the [REST](https://morioh.com/topic/rest API. The list below shows the API definition from Part 1 but is updated to use the person_id variable in the URL path:

Where the URL definitions required an lname value, they now require the person_id (primary key) for the person record in the people table. This allows you to remove the code in the previous app that artificially restricted users from editing a person’s last name.

In order for you to implement these changes, the swagger.yml file from Part 1 will have to be edited. For the most part, any lname parameter value will be changed to person_id, and person_id will be added to the POST and PUT responses. You can check out the updated swagger.yml file.

Update the REST API Handlers

With the swagger.yml file updated to support the use of the person_id identifier, you’ll also need to update the handlers in the people.py file to support these changes. In the same way that the swagger.yml file was updated, you need to change the people.py file to use the person_id value rather than lname.

Here’s part of the updated person.py module showing the handler for the REST URL endpoint GET /api/people:

from flask import (

    make_response,

    abort,

)

from config import db

from models import (

    Person,

    PersonSchema,

)


def read_all():

    """

    This function responds to a request for /api/people

    with the complete lists of people


    :return:        json string of list of people

    """

    # Create the list of people from our data

    people = Person.query \

        .order_by(Person.lname) \

        .all()


    # Serialize the data for the response

    person_schema = PersonSchema(many=True)

    return person_schema.dump(people).data

Here’s what the above code is doing:

  • Lines 1 – 9 import some Flask modules to create the REST API responses, as well as importing the db instance from the config.py module. In addition, it imports the SQLAlchemy Person and Marshmallow PersonSchema classes to access the person database table and serialize the results.

  • Line 11 starts the definition of read_all() that responds to the REST API URL endpoint GET /api/people and returns all the records in the person database table sorted in ascending order by last name.

  • Lines 19 – 22 tell SQLAlchemy to query the person database table for all the records, sort them in ascending order (the default sorting order), and return a list of Person Python objects as the variable people.

  • Line 24 is where the Marshmallow PersonSchema class definition becomes valuable. You create an instance of the PersonSchema, passing it the parameter many=True. This tells PersonSchema to expect an interable to serialize, which is what the people variable is.

  • Line 25 uses the PersonSchema instance variable (person_schema), calling its dump() method with the people list. The result is an object having a data attribute, an object containing a people list that can be converted to JSON. This is returned and converted by Connexion to JSON as the response to the REST API call.

Note: The people list variable created on Line 24 above can’t be returned directly because Connexion won’t know how to convert the timestamp field into JSON. Returning the list of people without processing it with Marshmallow results in a long error traceback and finally this Exception:

TypeError: Object of type Person is not JSON serializable
Here’s another part of the `person.py` module that makes a request for a single person from the `person` database. Here, `read_one(person_id)` function receives a `person_id` from the REST URL path, indicating the user is looking for a specific person. Here’s part of the updated `person.py` module showing the handler for the REST URL endpoint `GET /api/people/{person_id}`:
def read_one(person_id):

    """

    This function responds to a request for /api/people/{person_id}

    with one matching person from people


    :param person_id:   ID of person to find

    :return:            person matching ID

    """

    # Get the person requested

    person = Person.query \

        .filter(Person.person_id == person_id) \

        .one_or_none()


    # Did we find a person?

    if person is not None:


        # Serialize the data for the response

        person_schema = PersonSchema()

        return person_schema.dump(person).data


    # Otherwise, nope, didn't find that person

    else:

        abort(404, 'Person not found for Id: {person_id}'.format(person_id=person_id))

Here’s what the above code is doing:

  • Lines 10 – 12 use the person_id parameter in a SQLAlchemy query using the filter method of the query object to search for a person with a person_id attribute matching the passed-in person_id. Rather than using the all() query method, use the one_or_none() method to get one person, or return None if no match is found.

  • Line 15 determines whether a person was found or not.

  • Line 17 shows that, if person was not None (a matching person was found), then serializing the data is a little different. You don’t pass the many=True parameter to the creation of the PersonSchema() instance. Instead, you pass many=False because only a single object is passed in to serialize.

  • Line 18 is where the dump method of person_schema is called, and the data attribute of the resulting object is returned.

  • Line 23 shows that, if person was None (a matching person wasn’t found), then the Flask abort() method is called to return an error.

Another modification to person.py is creating a new person in the database. This gives you an opportunity to use the Marshmallow PersonSchema to deserialize a JSON structure sent with the HTTP request to create a SQLAlchemy Person object. Here’s part of the updated person.py module showing the handler for the REST URL endpoint POST /api/people:

def create(person):

    """

    This function creates a new person in the people structure

    based on the passed-in person data


    :param person:  person to create in people structure

    :return:        201 on success, 406 on person exists

    """

    fname = person.get('fname')

    lname = person.get('lname')


    existing_person = Person.query \

        .filter(Person.fname == fname) \

        .filter(Person.lname == lname) \

        .one_or_none()


    # Can we insert this person?

    if existing_person is None:


        # Create a person instance using the schema and the passed-in person

        schema = PersonSchema()

        new_person = schema.load(person, session=db.session).data


        # Add the person to the database

        db.session.add(new_person)

        db.session.commit()


        # Serialize and return the newly created person in the response

        return schema.dump(new_person).data, 201


    # Otherwise, nope, person exists already

    else:

        abort(409, f'Person {fname} {lname} exists already')

Here’s what the above code is doing:

  • Line 9 & 10 set the fname and lname variables based on the Person data structure sent as the POST body of the HTTP request.

  • Lines 12 – 15 use the SQLAlchemy Person class to query the database for the existence of a person with the same fname and lname as the passed-in person.

  • Line 18 addresses whether existing_person is None. (existing_person was not found.)

  • Line 21 creates a PersonSchema() instance called schema.

  • Line 22 uses the schema variable to load the data contained in the person parameter variable and create a new SQLAlchemy Person instance variable called new_person.

  • Line 25 adds the new_person instance to the db.session.

  • Line 26 commits the new_person instance to the database, which also assigns it a new primary key value (based on the auto-incrementing integer) and a UTC-based timestamp.

  • Line 33 shows that, if existing_person is not None (a matching person was found), then the Flask abort() method is called to return an error.

Update the Swagger UI

With the above changes in place, your REST API is now functional. The changes you’ve made are also reflected in an updated swagger UI interface and can be interacted with in the same manner. Below is a screenshot of the updated swagger UI opened to the GET /people/{person_id} section. This section of the UI gets a single person from the database and looks like this:

As shown in the above screenshot, the path parameter lname has been replaced by person_id, which is the primary key for a person in the REST API. The changes to the UI are a combined result of changing the swagger.yml file and the code changes made to support that.

Update the Web Application

The REST API is running, and CRUD operations are being persisted to the database. So that it is possible to view the demonstration web application, the JavaScript code has to be updated.

The updates are again related to using person_id instead of lname as the primary key for person data. In addition, the person_id is attached to the rows of the display table as HTML data attributes named data-person-id, so the value can be retrieved and used by the JavaScript code.

This article focused on the database and making your REST API use it, which is why there’s just a link to the updated JavaScript source and not much discussion of what it does.

Example Code

All of the example code for this article is available here. There’s one version of the code containing all the files, including the build_database.py utility program and the server.py modified example program from Part 1.

Conclusion

Congratulations, you’ve covered a lot of new material in this article and added useful tools to your arsenal!

You’ve learned how to save Python objects to a database using SQLAlchemy. You’ve also learned how to use Marshmallow to serialize and deserialize SQLAlchemy objects and use them with a JSON REST API. The things you’ve learned have certainly been a step up in complexity from the simple REST API of Part 1, but that step has given you two very powerful tools to use when creating more complex applications.

SQLAlchemy and Marshmallow are amazing tools in their own right. Using them together gives you a great leg up to create your own web applications backed by a database.

Python REST APIs With Flask, Connexion, and SQLAlchemy – Part 3

In Part 3 of this series, you’ll focus on the R part of RDBMS: relationships, which provide even more power when you are using a database.

That’s only part of the power a database offers, however. An even more powerful feature is the R part of RDBMS systems: relationships. In a database, a relationship is the ability to connect two or more tables together in a meaningful way. In this article, you’ll learn how to implement relationships and turn your Person database into a mini-blogging web application.

In this article, you’ll learn:

  • Why more than one table in a database is useful and important
  • How tables are related to each other
  • How SQLAlchemy can help you manage relationships
  • How relationships help you build a mini-blogging application

Table of Contents

  • Who This Article Is For
  • Additional Dependencies
  • People Data Extended for Blogging
  • Brute Force Approach
    • Advantages
    • Disadvantages
  • Relational Database Approach
    • Advantages
    • Disadvantages
  • SQLAlchemy Models
  • Initialize the Database
  • Update REST API
  • Implement the API
    • Update Response JSON
    • People
    • Notes
  • Updated Swagger UI
  • Mini-Blogging Web Application
    • Navigation
    • Home Page
    • People Page
    • Notes Page
    • Web Application
  • Conclusion

Who This Article Is For

This article expands your programming tool belt further. You’ll learn how to create hierarchical data structures represented as one-to-many relationships by SQLAlchemy. In addition, you’ll extend the REST API you’ve already built to provide CRUD (Create, Read, Update, and Delete) support for the elements in this hierarchical structure.

The web application presented in Part 2 will have its HTML and JavaScript files modified in major ways to create a more fully functional mini-blogging application. You can review the final version of the code from Part 2 in the GitHub repository for that article.

Hang on as you get started creating relationships and your mini-blogging application!

Additional Dependencies

There are no new Python dependencies beyond what was required for the Part 2 article. However, you will be using two new JavaScript modules in the web application to makes things easier and more consistent. The two modules are the following:

  1. Handlebars.js is a templating engine for JavaScript, much like Jinja2 for Flask.
  2. Moment.js is a datetime parsing and formatting module that makes displaying UTC timestamps easier.

You don’t have to download either of these, as the web application will get them directly from the Cloudflare CDN (Content Delivery Network), as you’re already doing for the jQuery module.

People Data Extended for Blogging

In Part 2, the People data existed as a dictionary in the build_database.py Python code. This is what you used to populate the database with some initial data. You’re going to modify the People data structure to give each person a list of notes associated with them. The new People data structure will look like this:

# Data to initialize database with
PEOPLE = [
    {
        "fname": "Doug",
        "lname": "Farrell",
        "notes": [
            ("Cool, a mini-blogging application!", "2019-01-06 22:17:54"),
            ("This could be useful", "2019-01-08 22:17:54"),
            ("Well, sort of useful", "2019-03-06 22:17:54"),
        ],
    },
    {
        "fname": "Kent",
        "lname": "Brockman",
        "notes": [
            (
                "I'm going to make really profound observations",
                "2019-01-07 22:17:54",
            ),
            (
                "Maybe they'll be more obvious than I thought",
                "2019-02-06 22:17:54",
            ),
        ],
    },
    {
        "fname": "Bunny",
        "lname": "Easter",
        "notes": [
            ("Has anyone seen my Easter eggs?", "2019-01-07 22:47:54"),
            ("I'm really late delivering these!", "2019-04-06 22:17:54"),
        ],
    },
]

Each person in the People dictionary now includes a key called notes, which is associated with a list containing tuples of data. Each tuple in the notes list represents a single note containing the content and a timestamp. The timestamps are initialized (rather than dynamically created) to demonstrate ordering later on in the REST API.

Each single person is associated with multiple notes, and each single note is associated with only one person. This hierarchy of data is known as a one-to-many relationship, where a single parent object is related to many child objects. You’ll see how this one-to-many relationship is managed in the database with SQLAlchemy.

Brute Force Approach

The database you built stored the data in a table, and a table is a two-dimensional array of rows and columns. Can the People dictionary above be represented in a single table of rows and columns? It can be, in the following way, in your person database table. Unfortunately to include all of the actual data in the example creates a scroll bar for the table, as you’ll see below:

The above table would actually work. All the data is represented, and a single person is associated with a collection of different notes.

Advantages

Conceptually, the above table structure has the advantage of being relatively simple to understand. You could even make the case that the data could be persisted to a flat file instead of a database.

Because of the two-dimensional table structure, you could store and use this data in a spreadsheet. Spreadsheets have been pressed into service as data storage quite a bit.

Disadvantages

While the above table structure would work, it has some real disadvantages.

In order to represent the collection of notes, all the data for each person is repeated for every unique note, the person data is therefore redundant. This isn’t such a big deal for your person data as there aren’t that many columns. But imagine if a person had many more columns. Even with large disk drives, this can get to be a storage concern if you’re dealing with millions of rows of data.

Having redundant data like this can lead to maintenance issues as time goes by. For example, what if the Easter Bunny decided a change of name was a good idea. In order to do this, every record containing the Easter Bunny’s name would have to be updated in order to keep the data consistent. This kind of work against the database can lead to data inconsistency, particularly if the work is done by a person running a SQL query by hand.

Naming columns becomes awkward. In the table above, there is a timestamp column used to track the creation and update time of a person in the table. You also want to have similar functionality for the creation and update time for a note, but because timestamp is already used, a contrived name of note_timestamp is used.

What if you wanted to add additional one-to-many relationships to the person table? For example, to include a person’s children or phone numbers. Each person could have multiple children and multiple phone numbers. This could be done relatively easily to the Python People dictionary above by adding children and phone_numbers keys with new lists containing the data.

However, representing those new one-to-many relationships in your person database table above becomes significantly more difficult. Every new one-to-many relationship increases the number of rows necessary to represent it for every single entry in the child data dramatically. In addition, the problems associated with data redundancy get bigger and more difficult to handle.

Lastly, the data you’d get back from the above table structure wouldn’t be very Pythonic: it would be just a big list of lists. SQLAlchemy wouldn’t be able to help you very much because the relationship isn’t there.

Relational Database Approach

Based on what you’ve seen above, it becomes clear that trying to represent even a moderately complex dataset in a single table becomes unmanageable pretty quickly. Given that, what alternative does a database offer? This is where the R part of RDBMS databases comes into play. Representing relationships removes the disadvantages outlined above.

Instead of trying to represent hierarchical data in a single table, the data is broken up into multiple tables, with a mechanism to relate them to one another. The tables are broken along collection lines, so for your People dictionary above, this means there will be a table representing people and another representing notes. This brings back your original person table, which looks like this:

To represent the new note information, you’ll create a new table called note. (Remember our singular table naming convention.) The table looks like this:

Notice that, like the person table, the note table has a unique identifier called note_id, which is the primary key for the note table. One thing that isn’t obvious is the inclusion of the person_id value in the table. What is that used for? This is what creates the relationship to the person table. Whereas note_id is the primary key for the table, person_id is what’s known as a foreign key.

The foreign key gives each entry in the note table the primary key of the person record it’s associated with. Using this, SQLAlchemy can gather all the notes associated with each person by connecting the person.person_id primary key to the note.person_id foreign key, creating a relationship.

Advantages

By breaking the data set into two tables, and introducing the concept of a foreign key, you’ve made the data a little more complex to think about, you have resolved the disadvantages of a single table representation. SQLAlchemy will help you encode the increased complexity fairly easily.

The data is no longer redundant in the database. There is only one person entry for each person you want to store in the database. This solves the storage concern immediately and dramatically simplifies the maintenance concerns.

If the Easter Bunny still wanted to change names, then you’d only have to change a single row in the person table, and anything else related to that row (like the note table) would immediately take advantage of the change.

Column naming is more consistent and meaningful. Because person and note data exist in separate tables, the creation and update timestamp can be named consistently in both tables, as there is no conflict for names across tables.

In addition, you’d no longer have to create permutations of each row for new one-to-many relationships you might want to represent. Take our children and phone_numbers example from earlier. Implementing this would require child and phone_number tables. Each table would contain a foreign key of person_id relating it back to the person table.

Using SQLAlchemy, the data you’d get back from the above tables would be more immediately useful, as what you’d get is an object for each person row. That object has named attributes equivalent to the columns in the table. One of those attributes is a Python list containing the related note objects.

Disadvantages

Where the brute force approach was simpler to understand, the concept of foreign keys and relationships make thinking about the data somewhat more abstract. This abstraction needs to be thought about for every relationship you establish between tables.

Making use of relationships means committing to using a database system. This is another tool to install, learn, and maintain above and beyond the application that actually uses the data.

SQLAlchemy Models

To use the two tables above, and the relationship between them, you’ll need to create SQLAlchemy models that are aware of both tables and the relationship between them. Here’s the SQLAlchemy Person model from Part 2, updated to include a relationship to a collection of notes:

class Person(db.Model):

    __tablename__ = 'person'

    person_id = db.Column(db.Integer, primary_key=True)

    lname = db.Column(db.String(32))

    fname = db.Column(db.String(32))

    timestamp = db.Column(

        db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow

    )

    notes = db.relationship(

        'Note',

        backref='person',

        cascade='all, delete, delete-orphan',

        single_parent=True,

        order_by='desc(Note.timestamp)'

    )

Lines 1 to 8 of the above Python class look exactly like what you created before in Part 2. Lines 9 to 16 create a new attribute in the Person class called notes. This new notes attributes is defined in the following lines of code:

  • Line 9: Like the other attributes of the class, this line creates a new attribute called notes and sets it equal to an instance of an object called db.relationship. This object creates the relationship you’re adding to the Person class and is created with all of the parameters defined in the lines that follow.

  • Line 10: The string parameter 'Note' defines the SQLAlchemy class that the Person class will be related to. The Note class isn’t defined yet, which is why it’s a string here. This is a forward reference and helps handle problems that the order of definitions could cause when something is needed that isn’t defined until later in the code. The 'Note' string allows the Person class to find the Note class at runtime, which is after both Person and Note have been defined.

  • Line 11: The backref='person' parameter is trickier. It creates what’s known as a backwards reference in Note objects. Each instance of a Note object will contain an attribute called person. The person attribute references the parent object that a particular Note instance is associated with. Having a reference to the parent object (person in this case) in the child can be very useful if your code iterates over notes and has to include information about the parent. This happens surprisingly often in display rendering code.

  • Line 12: The cascade='all, delete, delete-orphan' parameter determines how to treat note object instances when changes are made to the parent Person instance. For example, when a Person object is deleted, SQLAlchemy will create the SQL necessary to delete the Person from the database. Additionally, this parameter tells it to also delete all the Note instances associated with it. You can read more about these options in the SQLAlchemy documentation.

  • Line 13: The single_parent=True parameter is required if delete-orphan is part of the previous cascade parameter. This tells SQLAlchemy not to allow orphaned Note instances (a Note without a parent Person object) to exist because each Note has a single parent.

  • Line 14: The order_by='desc(Note.timestamp)' parameter tells SQLAlchemy how to sort the Note instances associated with a Person. When a Person object is retrieved, by default the notes attribute list will contain Note objects in an unknown order. The SQLAlchemy desc(...) function will sort the notes in descending order from newest to oldest. If this line was instead order_by='Note.timestamp', SQLAlchemy would default to using the asc(...) function, and sort the notes in ascending order, oldest to newest.

Now that your Person model has the new notes attribute, and this represents the one-to-many relationship to Note objects, you’ll need to define a SQLAlchemy model for a Note:

class Note(db.Model):

    __tablename__ = 'note'

    note_id = db.Column(db.Integer, primary_key=True)

    person_id = db.Column(db.Integer, db.ForeignKey('person.person_id'))

    content = db.Column(db.String, nullable=False)

    timestamp = db.Column(

        db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow

    )

The Note class defines the attributes making up a note as seen in our sample note database table from above. The attributes are defined here:

  • Line 1 creates the Note class, inheriting from db.Model, exactly as you did before when creating the Person class.

  • Line 2 tells the class what database table to use to store Note objects.

  • Line 3 creates the note_id attribute, defining it as an integer value, and as the primary key for the Note object.

  • Line 4 creates the person_id attribute, and defines it as the foreign key, relating the Note class to the Person class using the person.person_id primary key. This, and the Person.notes attribute, are how SQLAlchemy knows what to do when interacting with Person and Note objects.

  • Line 5 creates the content attribute, which contains the actual text of the note. The nullable=False parameter indicates that it’s okay to create new notes that have no content.

  • Line 6 creates the timestamp attribute, and exactly like the Person class, this contains the creation or update time for any particular Note instance.

Initialize the Database

Now that you’ve updated the Person and created the Note models, you’ll use them to rebuild the testing database people.db. You’ll do this by updating the build_database.py code from Part 2. Here’s what the code will look like:

import os

from datetime import datetime

from config import db

from models import Person, Note


# Data to initialize database with

PEOPLE = [

    {

        "fname": "Doug",

        "lname": "Farrell",

        "notes": [

            ("Cool, a mini-blogging application!", "2019-01-06 22:17:54"),

            ("This could be useful", "2019-01-08 22:17:54"),

            ("Well, sort of useful", "2019-03-06 22:17:54"),

        ],

    },

    {

        "fname": "Kent",

        "lname": "Brockman",

        "notes": [

            (

                "I'm going to make really profound observations",

                "2019-01-07 22:17:54",

            ),

            (

                "Maybe they'll be more obvious than I thought",

                "2019-02-06 22:17:54",

            ),

        ],

    },

    {

        "fname": "Bunny",

        "lname": "Easter",

        "notes": [

            ("Has anyone seen my Easter eggs?", "2019-01-07 22:47:54"),

            ("I'm really late delivering these!", "2019-04-06 22:17:54"),

        ],

    },

]


# Delete database file if it exists currently

if os.path.exists("people.db"):

    os.remove("people.db")


# Create the database

db.create_all()


# Iterate over the PEOPLE structure and populate the database

for person in PEOPLE:

    p = Person(lname=person.get("lname"), fname=person.get("fname"))


    # Add the notes for the person

    for note in person.get("notes"):

        content, timestamp = note

        p.notes.append(

            Note(

                content=content,

                timestamp=datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S"),

            )

        )

    db.session.add(p)


db.session.commit()

The code above came from Part 2, with a few changes to create the one-to-many relationship between Person and Note. Here are the updated or new lines added to the code:

  • Line 4 has been updated to import the Note class defined previously.

  • Lines 7 to 39 contain the updated PEOPLE dictionary containing our person data, along with the list of notes associated with each person. This data will be inserted into the database.

  • Lines 49 to 61 iterate over the PEOPLE dictionary, getting each person in turn and using it to create a Person object.

  • Line 53 iterates over the person.notes list, getting each note in turn.

  • Line 54 unpacks the content and timestamp from each note tuple.

  • Line 55 to 60 creates a Note object and appends it to the person notes collection using p.notes.append().

  • Line 61 adds the Person object p to the database session.

  • Line 63 commits all of the activity in the session to the database. It’s at this point that all of the data is written to the person and note tables in the people.db database file.

You can see that working with the notes collection in the Person object instance p is just like working with any other list in Python. SQLAlchemy takes care of the underlying one-to-many relationship information when the db.session.commit() call is made.

For example, just like a Person instance has its primary key field person_id initialized by SQLAlchemy when it’s committed to the database, instances of Note will have their primary key fields initialized. In addition, the Note foreign key person_id will also be initialized with the primary key value of the Person instance it’s associated with.

Here’s an example instance of a Person object before the db.session.commit() in a kind of pseudocode:

Person (
    person_id = None
    lname = 'Farrell'
    fname = 'Doug'
    timestamp = None
    notes = [
        Note (
            note_id = None
            person_id = None
            content = 'Cool, a mini-blogging application!'
            timestamp = '2019-01-06 22:17:54'
        ),
        Note (
            note_id = None
            person_id = None
            content = 'This could be useful'
            timestamp = '2019-01-08 22:17:54'
        ),
        Note (
            note_id = None
            person_id = None
            content = 'Well, sort of useful'
            timestamp = '2019-03-06 22:17:54'
        )
    ]
)

Here’s the example Person object after the db.session.commit():

Person (
    person_id = 1
    lname = 'Farrell'
    fname = 'Doug'
    timestamp = '2019-02-02 21:27:10.336'
    notes = [
        Note (
            note_id = 1
            person_id = 1
            content = 'Cool, a mini-blogging application!'
            timestamp = '2019-01-06 22:17:54'
        ),
        Note (
            note_id = 2
            person_id = 1
            content = 'This could be useful'
            timestamp = '2019-01-08 22:17:54'
        ),
        Note (
            note_id = 3
            person_id = 1
            content = 'Well, sort of useful'
            timestamp = '2019-03-06 22:17:54'
        )
    ]
)

The important difference between the two is that the primary key of the Person and Note objects has been initialized. The database engine took care of this as the objects were created because of the auto-incrementing feature of primary keys discussed in Part 2.

Additionally, the person_id foreign key in all the Note instances has been initialized to reference its parent. This happens because of the order in which the Person and Note objects are created in the database.

SQLAlchemy is aware of the relationship between Person and Note objects. When a Person object is committed to the person database table, SQLAlchemy gets the person_id primary key value. That value is used to initialize the foreign key value of person_id in a Note object before it’s committed to the database.

SQLAlchemy takes care of this database housekeeping work because of the information you passed when the Person.notes attribute was initialized with the db.relationship(...) object.

In addition, the Person.timestamp attribute has been initialized with the current timestamp.

Running the build_database.py program from the command line (in the virtual environment will re-create the database with the new additions, getting it ready for use with the web application. This command line will rebuild the database:

$ python build_database.py

The build_database.py utility program doesn’t output any messages if it runs successfully. If it throws an exception, then an error will be printed on the screen.

Update REST API

You’ve updated the SQLAlchemy models and used them to update the people.db database. Now it’s time to update the REST API to provide access to the new notes information. Here’s the REST API you built in Part 2:

The REST API above provides HTTP URL paths to collections of things, and to the things themselves. You can get a list of people or interact with a single person from that list of people. This path style refines what’s returned in a left-to-right manner, getting more granular as you go.

You’ll continue this left-to-right pattern to get more granular and access the notes collections. Here’s the extended REST API you’ll create in order to provide notes to the mini-blog web application:

There are two variations in the notes part of the REST API compared to the convention used in the people section:

  1. There is no URL defined to get all the notes associated with a person, only a URL to get a single note. This would have made the REST API complete in a way, but the web application you’ll create later doesn’t need this functionality. Therefore, it’s been left out.

  2. There is the inclusion of the last URL /api/notes. This is a convenience method created for the web application. It will be used in the mini-blog on the home page to show all the notes in the system. There isn’t a way to get this information readily using the REST API pathing style as designed, so this shortcut has been added.

As in Part 2, the REST API is configured in the swagger.yml file.

Note:

The idea of designing a REST API with a path that gets more and more granular as you move from left to right is very useful. Thinking this way can help clarify the relationships between different parts of a database. Just be aware that there are realistic limits to how far down a hierarchical structure this kind of design should be taken.

For example, what if the Note object had a collection of its own, something like comments on the notes. Using the current design ideas, this would lead to a URL that went something like this: /api/people/{person_id}/notes/{note_id}/comments/{comment_id}

There is no practical limit to this kind of design, but there is one for usefulness. In actual use in real applications, a long, multilevel URL like that one is hardly ever needed. A more common pattern is to get a list of intervening objects (like notes) and then use a separate API entry point to get a single comment for an application use case.

Implement the API

With the updated REST API defined in the swagger.yml file, you’ll need to update the implementation provided by the Python modules. This means updating existing module files, like models.py and people.py, and creating a new module file called notes.py to implement support for Notes in the extended REST API.

Update Response JSON

The purpose of the REST API is to get useful JSON data out of the database. Now that you’ve updated the SQLAlchemy Person and created the Note models, you’ll need to update the Marshmallow schema models as well. As you may recall from Part 2, Marshmallow is the module that translates the SQLAlchemy objects into Python objects suitable for creating JSON strings.

The updated and newly created Marshmallow schemas are in the models.py module, which are explained below, and look like this:

class PersonSchema(ma.ModelSchema):

    class Meta:

        model = Person

        sqla_session = db.session

    notes = fields.Nested('PersonNoteSchema', default=[], many=True)


class PersonNoteSchema(ma.ModelSchema):

    """

    This class exists to get around a recursion issue

    """

    note_id = fields.Int()

    person_id = fields.Int()

    content = fields.Str()

    timestamp = fields.Str()


class NoteSchema(ma.ModelSchema):

    class Meta:

        model = Note

        sqla_session = db.session

    person = fields.Nested('NotePersonSchema', default=None)


class NotePersonSchema(ma.ModelSchema):

    """

    This class exists to get around a recursion issue

    """

    person_id = fields.Int()

    lname = fields.Str()

    fname = fields.Str()

    timestamp = fields.Str()

There are some interesting things going on in the above definitions. The PersonSchema class has one new entry: the notes attribute defined in line 5. This defines it as a nested relationship to the PersonNoteSchema. It will default to an empty list if nothing is present in the SQLAlchemy notes relationship. The many=True parameter indicates that this is a one-to-many relationship, so Marshmallow will serialize all the related notes.

The PersonNoteSchema class defines what a Note object looks like as Marshmallow serializes the notes list. The NoteSchema defines what a SQLAlchemy Note object looks like in terms of Marshmallow. Notice that it has a person attribute. This attribute comes from the SQLAlchemy db.relationship(...) definition parameter backref='person'. The person Marshmallow definition is nested, but because it doesn’t have the many=True parameter, there is only a single person connected.

The NotePersonSchema class defines what is nested in the NoteSchema.person attribute.

Note:

You might be wondering why the PersonSchema class has its own unique PersonNoteSchema class to define the notes collection attribute. By the same token, the NoteSchema class has its own unique NotePersonSchema class to define the person attribute. You may be wondering whether the PersonSchema class could be defined this way:

class PersonSchema(ma.ModelSchema):
    class Meta:
        model = Person
        sqla_session = db.session
    notes = fields.Nested('NoteSchema', default=[], many=True)

Additionally, couldn’t the NoteSchema class be defined using the PersonSchema to define the person attribute? A class definition like this would each refer to the other, and this causes a recursion error in Marshmallow as it will cycle from PersonSchema to NoteSchema until it runs out of stack space. Using the unique schema references breaks the recursion and allows this kind of nesting to work.

People

Now that you’ve got the schemas in place to work with the one-to-many relationship between Person and Note, you need to update the person.py and create the note.py modules in order to implement a working REST API.

The people.py module needs two changes. The first is to import the Note class, along with the Person class at the top of the module. Then only read_one(person_id) needs to change in order to handle the relationship. That function will look like this:

def read_one(person_id):

    """

    This function responds to a request for /api/people/{person_id}

    with one matching person from people


    :param person_id:   Id of person to find

    :return:            person matching id

    """

    # Build the initial query

    person = (

        Person.query.filter(Person.person_id == person_id)

        .outerjoin(Note)

        .one_or_none()

    )


    # Did we find a person?

    if person is not None:


        # Serialize the data for the response

        person_schema = PersonSchema()

        data = person_schema.dump(person).data

        return data


    # Otherwise, nope, didn't find that person

    else:

        abort(404, f"Person not found for Id: {person_id}")

The only difference is line 12: .outerjoin(Note). An outer join (left outer join in SQL terms) is necessary for the case where a user of the application has created a new person object, which has no notes related to it. The outer join ensures that the SQL query will return a person object, even if there are no note rows to join with.

At the start of this article, you saw how person and note data could be represented in a single, flat table, and all of the disadvantages of that approach. You also saw the advantages of breaking that data up into two tables, person and note, with a relationship between them.

Until now, we’ve been working with the data as two distinct, but related, items in the database. But now that you’re actually going to use the data, what we essentially want is for the data to be joined back together. This is what a database join does. It combines data from two tables together using the primary key to foreign key relationship.

A join is kind of a boolean and operation because it only returns data if there is data in both tables to combine. If, for example, a person row exists but has no related note row, then there is nothing to join, so nothing is returned. This isn’t what you want for read_one(person_id).

This is where the outer join comes in handy. It’s a kind of boolean or operation. It returns person data even if there is no associated note data to combine with. This is the behavior you want for read_one(person_id) to handle the case of a newly created Person object that has no notes yet.

You can see the complete people.py in the article repository.

Notes

You’ll create a notes.py module to implement all the Python code associated with the new note related REST API definitions. In many ways, it works like the people.py module, except it must handle both a person_id and a note_id as defined in the swagger.yml configuration file. As an example, here is read_one(person_id, note_id):

def read_one(person_id, note_id):

    """

    This function responds to a request for

    /api/people/{person_id}/notes/{note_id}

    with one matching note for the associated person


    :param person_id:       Id of person the note is related to

    :param note_id:         Id of the note

    :return:                json string of note contents

    """

    # Query the database for the note

    note = (

        Note.query.join(Person, Person.person_id == Note.person_id)

        .filter(Person.person_id == person_id)

        .filter(Note.note_id == note_id)

        .one_or_none()

    )


    # Was a note found?

    if note is not None:

        note_schema = NoteSchema()

        data = note_schema.dump(note).data

        return data


    # Otherwise, nope, didn't find that note

    else:

        abort(404, f"Note not found for Id: {note_id}")

The interesting parts of the above code are lines 12 to 17:

  • Line 13 begins a query against the Note SQLAlchemy objects and joins to the related Person SQLAlchemy object comparing person_id from both Person and Note.
  • Line 14 filters the result down to the Note objects that has a Person.person_id equal to the passed in person_id parameter.
  • Line 15 filters the result further to the Note object that has a Note.note_id equal to the passed in note_id parameter.
  • Line 16 returns the Note object if found, or None if nothing matching the parameters is found.

You can check out the complete notes.py.

Updated Swagger UI

The Swagger UI has been updated by the action of updating the swagger.yml file and creating the URL endpoint implementations. Below is a screenshot of the updated UI showing the Notes section with the GET /api/people/{person_id}/notes/{note_id} expanded:

Mini-Blogging Web Application

The web application has been substantially changed to show its new purpose as a mini-blogging application. It has three pages:

  1. The home page (localhost:5000/), which shows all of the blog messages (notes) sorted from newest to oldest

  2. The people page (localhost:5000/people), which shows all the people in the system, sorted by last name, and also allows the user to create a new person and update or delete an existing one

  3. The notes page (localhost:5000/people/{person_id}/notes), which shows all the notes associated with a person, sorted from newest to oldest, and also allows the user to create a new note and update or delete an existing one

[Remove ads](/account/join/)

Navigation

There are two buttons on every page of the application:

  1. The Home button will navigate to the home screen.
  2. The People button navigates to the /people screen, showing all people in the database.

These two buttons are present on every screen in the application as a way to get back to a starting point.

Home Page

Below is a screenshot of the home page showing the initialized database contents:

The functionality of this page works like this:

  • Double-clicking on a person’s name will take the user to the /people/{person_id} page, with the editor section filled in with the person’s first and last names and the update and reset buttons enabled.

  • Double-clicking on a person’s note will take the user to the /people/{person_id}/notes/{note_id} page, with the editor section filled in with the note’s contents and the Update and Reset buttons enabled.

People Page

Below is a screenshot of the people page showing the people in the initialized database:

The functionality of this page works like this:

  • Single-clicking on a person’s name will populate the editor section of the page with the person’s first and last name, disabling the Create button, and enabling the Update and Delete buttons.

  • Double clicking on a person’s name will navigate to the notes pages for that person.

The functionality of the editor works like this:

  • If the first and last name fields are empty, the Create and Reset buttons are enabled. Entering a new name in the fields and clicking Create will create a new person and update the database and re-render the table below the editor. Clicking Reset will clear the editor fields.

  • If the first and last name fields have data, the user navigated here by double-clicking the person’s name from the home screen. In this case, the Update, Delete, and Reset buttons are enabled. Changing the first or last name and clicking Update will update the database and re-render the table below the editor. Clicking Delete will remove the person from the database and re-render the table.

Notes Page

Below is a screenshot of the notes page showing the notes for a person in the initialized database:

The functionality of this page works like this:

  • Single-clicking on a note will populate the editor section of the page with the notes content, disabling the Create button, and enabling the Update and Delete buttons.

  • All other functionality of this page is in the editor section.

The functionality of the editor works like this:

  • If the note content field is empty, then the Create and Reset buttons are enabled. Entering a new note in the field and clicking Create will create a new note and update the database and re-render the table below the editor. Clicking Reset will clear the editor fields.

  • If the note field has data, the user navigated here by double-clicking the person’s note from the home screen. In this case, the Update, Delete, and Reset buttons are enabled. Changing the note and clicking Update will update the database and re-render the table below the editor. Clicking Delete will remove the note from the database and re-render the table.

Web Application

This article is primarily focused on how to use SQLAlchemy to create relationships in the database, and how to extend the REST API to take advantage of those relationships. As such, the code for the web application didn’t get much attention. When you look at the web application code, keep an eye out for the following features:

  • Each page of the application is a fully formed single page web application.

  • Each page of the application is driven by JavaScript following an MVC (Model/View/Controller) style of responsibility delegation.

  • The HTML that creates the pages takes advantage of the Jinja2 inheritance functionality.

  • The hardcoded JavaScript table creation has been replaced by using the Handlebars.js templating engine.

  • The timestamp formating in all of the tables is provided by Moment.js.

You can find the following code in the repository for this article:

All of the example code for this article is available in the GitHub repository for this article. This contains all of the code related to this article, including all of the web application code.

Conclusion

Congratulations are in order for what you’ve learned in this article! Knowing how to build and use database relationships gives you a powerful tool to solve many difficult problems. There are other relationship besides the one-to-many example from this article. Other common ones are one-to-one, many-to-many, and many-to-one. All of them have a place in your toolbelt, and SQLAlchemy can help you tackle them all!

Python REST APIs With Flask, Connexion, and SQLAlchemy – Part 4

Table of Contents

In Part 3 of this series, you added relationships to the REST API and to the database that supports it. This gave you a powerful tool you can use to build interesting programs that send persistent data, as well as the relationships between that data, to a database system. Having a REST API gives you the ability to create a Single-Page Application (SPA) with HTML, CSS, and JavaScript. This is a good starting point before you make the jump to more powerful front-end frameworks, like Angular or React.

In this article, you’ll learn how to:

  • Structure an HTML file to act as the template of a single-page web application
  • Use Cascading Style Sheets (CSS) to style the presentation of an application
  • Use native JavaScript to add interactivity to an application
  • Use JavaScript to make HTTP AJAX requests to the REST API you developed in Part 3 of this series

You can get all of the code you’ll see in this tutorial at the link below:

Who This Article Is For

Part 1 of this series guided you through building a REST API, and Part 2 showed you how to connect that REST API to a database. In Part 3, you added relationships to the REST API and the supporting database.

This article is about presenting that REST API to a user as a browser-based web application. This combination gives you both front-end and back-end abilities, which is a useful and powerful skill set.

Creating Single-Page Applications

In Part 3 of this series, you added relationships to the REST API and database to represent notes associated with people. In other words, you created a kind of mini-blog. The web applications you built in part 3 showed you one way to present and interact with the REST API. You navigated between three Single-Page Applications (SPA) to access different parts of the REST API.

While you could have combined that functionality into a single SPA, that approach would make the concepts of styling and interactivity more complex, without much added value. For that reason, each page was a complete, standalone SPA.

In this article, you’ll focus on the People SPA, which presents the list of people in the database and provides an editor feature to create new people and update or delete existing ones. The Home and Notes pages are conceptually similar.

What Existing Frameworks Are There?

There are existing libraries that provide inbuilt and robust functionality for creating SPA systems. For example, the Bootstrap library provides a popular framework of styles for creating consistent and good-looking web applications. It has JavaScript extensions that add interactivity to the styled DOM elements.

There are also powerful web application frameworks, like React and Angular, that give you complete web application development systems. These are useful when you want to create large, multi-page SPAs that would be cumbersome to build from scratch.

Why Build Your Own?

With the availability of tools like those listed above, why would you choose to create an SPA from scratch? Take Bootstrap, for instance. You can use it to create SPAs that look excellent, and you can certainly use it with your JavaScript code!

The problem is that Bootstrap has a steep learning curve you’ll need to climb if you want to use it well. It also adds a lot of Bootstrap-specific attributes to the DOM elements defined in your HTML content. Likewise, tools like React and Angular also have significant learning curves you’ll need to overcome. However, there’s still a place for web applications that don’t rely on tools like these.

Often, when you’re building a web application, you want to build a proof-of-concept first to see if the application is at all useful. You’ll want to get this up and running quickly, so it can be faster for you to roll your own prototype and upgrade it later. Since you won’t invest much time in the prototype, it won’t be too costly to start over and create a new application with a supported, fully-featured framework.

There’s a gap between what you’re going to develop with the People app in this article and what you could build with a complete framework. It’s up to you to decide where the tipping point is between providing the functionality yourself or adopting a framework.

Parts of Single-Page Applications

There are a few main forms of interactivity in traditional web-based systems. You can navigate between pages and submit a page with new information. You can fill out forms containing input fields, radio buttons, checkboxes, and more. When you perform these activities, the webserver responds by sending new files to your browser. Then, your browser renders the content again.

Single-page applications break this pattern by loading everything they need first. Then, any interactivity or navigation is handled by JavaScript or by calls to the server behind the scenes. These activities update the page content dynamically.

There are three major components of a single-page application:

  1. HTML provides the content of a web page, or what is rendered by your browser.
  2. CSS provides the presentation, or style, of a web page. It defines how the content of the page should look when rendered by your browser.
  3. JavaScript provides the interactivity of a web page. It also handles communication with the back-end server.

Next, you’ll take a closer look at each of these major components.

HTML

HTML is a text file delivered to your browser that provides the primary content and structure for a single-page application. This structure includes the definitions for id and class attributes, which are used by CSS to style the content and JavaScript to interact with the structure. Your browser parses the HTML file to create the Document Object Model (DOM), which it uses to render the content to the display.

The markup within an HTML file includes tags, like paragraph tags <p>...</p> and header tags <h1>...</h1>. These tags become elements within the DOM as your browser parses the HTML and renders it to the display. The HTML file also contains links to external resources that your browser will load as it parses the HTML. For the SPA you’re building in this article, these external resources are CSS and JavaScript files.

CSS

Cascading Style Sheets (CSS) are files that contain styling information that will be applied to whatever DOM structure is rendered from an HTML file. In this way, the content of a web page can be separated from its presentation.

In CSS, the style for a DOM structure is determined by selectors. A selector is just a method of matching a style to elements within the DOM. For example, the p selector in the code block below applies styling information to all paragraph elements:

p {
    font-weight: bold;
    background-color: cyan;
}

The above style will apply to all paragraph elements in the DOM. The text will appear as bold and have a background color of cyan.

The cascading part of CSS means that styles defined later, or in a CSS file loaded after another, will take precedence over any previously defined style. For example, you can define a second paragraph style after the style above:

p {
    font-weight: bold;
    background-color: cyan;
}

p {
    background-color: cornflower;
}

This new style definition would modify the existing style so that all paragraph elements in the DOM will have a background color of cornflower. This overrides the background-color of the previous style, but it leaves the font-weight setting intact. You could also define the new paragraph style in a CSS file of its own.

The id and class attributes let you apply a style to specific individual elements in the DOM. For example, the HTML to render a new DOM might look like this:

<p>
    This is some introductory text
</p>

<p class="panel">
    This is some text contained within a panel
</p>

This will create two paragraph elements within the DOM. The first has no class attribute, but the second has a class attribute of panel. Then, you can create a CSS style like this:

p {
    font-weight: bold;
    width: 80%;
    margin-left: auto;
    margin-right: auto;
    background-color: lightgrey;
}

.panel {
    border: 1px solid darkgrey;
    border-radius: 4px;
    padding: 10px;
    background-color: lightskyblue;
}

Here, you define a style for any elements that have the panel attribute. When your browser renders the DOM, the two paragraph elements should look like this:

Both paragraph elements have the first style definition applied to them because the p selector selects them both. But only the second paragraph has the .panel style applied to it because it’s the only element with the class attribute panel that matches that selector. The second paragraph gets new styling information from the .panel style, and overrides the background-color style defined in the p style.

JavaScript

JavaScript provides all of the interactive features for an SPA, as well as dynamic communication with the REST API provided by the server. It also performs all of the updates to the DOM, allowing an SPA to act much like a full Graphical User Interface (GUI) application like Word or Excel.

As JavaScript has evolved, it’s become easier and more consistent to work with the DOM provided by modern browsers. You’ll be using a few conventions, like namespaces and separation of concerns, to help keep your JavaScript code from conflicting with other libraries you might include.

Note: You’ll be creating Single-Page Applications using native JavaScript. In particular, you’ll use the ES2017 version, which works with many modern browsers, but could be problematic if your goal is to support older browser versions.

Modules and Namespaces

You might already know about namespaces in Python and why they’re valuable. In short, namespaces give you a way to keep the names in your program unique to prevent conflicts. For example, if you wanted to use log() from both the math and cmath modules, then your code might look something like this:

>>> import math
>>> import cmath
>>> math.log(10)
2.302585092994046
>>> cmath.log(10)
(2.302585092994046+0j)

The Python code above imports both the math and cmath modules, then calls log(10) from each module. The first call returns a real number and the second returns a complex number, which cmath has functions for. Each instance of log() is unique to its own namespace (math or cmath), meaning the calls to log() don’t conflict with each other.

Modern JavaScript has the ability to import modules and assign namespaces to those modules. This is useful if you need to import other JavaScript libraries where there might be a name conflict.

If you look at the end of the people.js file, then you’ll see this:

// Create the MVC components

const model = new Model();

const view = new View();

const controller = new Controller(model, view);


// Export the MVC components as the default

export default {

    model,

    view,

    controller

};

The code above creates the three components of the MVC system, which you’ll see later on in this article. The default export from the module is a JavaScript literal object. You import this module at the bottom of the people.html file:

<script type="module">

    // Give the imported MVC components a namespace

    import * as MVC from "/static/js/people.js";


    // Create an intentional global variable referencing the import

    window.mvc = MVC;

</script>

Here’s how this code works:

  • Line 50 uses type="module" to tell the system that the file is a module and not just a JavaScript file.

  • Line 52 imports the default object from people.js and assigns it the name MVC. This creates a namespace called MVC. You can give the imported object any name that doesn’t conflict with other JavaScript libraries you might not have control over.

  • Line 55 creates a global variable, which is a convenient step. You can use this to inspect the mvc object with a JavaScript debugger and look at model, view, and controller.

Note: Because MVC is an imported module and not just an included file, JavaScript will default to strict mode, which has some advantages over non-strict mode. One of the biggest is that you can’t use undefined variables.

Without strict mode turned on, this is perfectly legal:

var myName = "Hello";
myNane = "Hello World";

Do you see the error? The first line creates a variable called myName and assigns the literal string "Hello" to it. The second line looks like it changes the contents of the variable to "Hello World", but that isn’t the case!

In the second line, "Hello World" is assigned to the variable name myNane, which is misspelled with an n. In non-strict JavaScript, this creates two variables:

  1. The correct variable myName
  2. The unintended typo version myNane

Imagine if these two lines of JavaScript code were separated by many others. This could create a run-time bug that’s difficult to find! When you use strict mode, you eliminate errors like this one by raising an exception should your code attempt to use an undeclared variable.

Naming Conventions

For the most part, the JavaScript code you’re using here is in camel case. This naming convention is widely used in the JavaScript community, so the code examples reflect that. However, your Python code will use snake case, which is more conventional in the Python community.

This difference in naming can be confusing where your JavaScript code interacts with Python code, and especially where shared variables enter the REST API interface. Be sure to keep these differences in mind as you write your code.

Separation of Concerns

The code that drives an SPA can be complicated. You can use the Model–View–Controller (MVC) architectural pattern to simplify things by creating a separation of concerns. The Home, People, and Notes SPAs use the following MVC pattern:

  • The Model provides all access to the server REST API. Anything presented on the display comes from the model. Any changes to the data go through the model and back to the REST API.

  • The View controls all display handling and DOM updates. The view is the only part of the SPA that interacts with the DOM and causes the browser to render and re-render any changes to the display.

  • The Controller handles all user interaction and any user data entered, like click events. Because the controller reacts to user input, it also interacts with the Model and View based on that user input.

In the illustration above, the Controller has a strong connection with both the Model and the View. Again, this is because any user interaction the Controller handles might require reaching out to the REST API to get or update data. It may even require updating the display.

The dotted line that goes from the Model to the Controller indicates a weak connection. Because calls to the REST API are asynchronous, the data that the Model provides to the Controller returns at a later time.

Creating the People SPA

Your mini-blog demonstration app has pages for Home, People, and Notes. Each of these pages is a complete, standalone SPA. They all use the same design and structure, so even though you’re focusing on the People application here, you’ll understand how to construct all of them.

People HTML

The Python Flask web framework provides the Jinja2 templating engine, which you’ll use for the People SPA. There are parts of the SPA that are common to all three pages, so each page uses the Jinja2 template inheritance feature to share those common elements.

You’ll provide the HTML content for the People SPA in two files: parent.html and people.html files. You can get the code for these files at the link below:

Here’s what your parent.html will look like:

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    {% block head %}

    <title>{% block title %}{% endblock %} Page</title>

    {% endblock %}

</head>

<body>

<div class="navigation">

    <span class="buttons">

        <a href="/">Home</a>

        <a href="/people">People</a>

    </span>

    <span class="page_name">

        <div></div>

    </span>

    <span class="spacer"></span>

</div>


{% block body %}

{% endblock %}

</body>


{% block javascript %}

{% endblock %}


</html>

parent.html has a few major elements:

  • Line 1 sets the document type as <!DOCTYPE html>. All new HTML pages begin with this declaration. Modern browsers know this means to use the HTML 5 standard, while older browsers will fall back to the latest standard they can support.
  • Line 4 tells the browser to use UTF-8 encoding.
  • Lines 10 to 19 define the navigation bar.
  • Lines 21 and 22 are Jinja2 block markers, which will be replaced by content in people.html.
  • Lines 25 and 26 are Jinja2 block markers that act as a placeholder for JavaScript code.

The people.html file will inherit the parent.html code. You can expand the code block below to see the whole file:

{% extends "parent.html" %}

{% block title %}People{% endblock %}

{% block head %}

{% endblock %}

{% block page_name %}Person Create/Update/Delete Page{% endblock %}


{% block body %}

    <div class="container">

        <input id="url_person_id" type="hidden" value="{{ person_id }}" />

        <div class="section editor">

            <div>

                <span>Person ID:</span>

                <span id="person_id"></span>

            </div>

            <label for="fname">First Name

                <input id="fname" type="text" />

            </label>

            <br />

            <label for="lname">Last Name

                <input id="lname" type="text" />

            </label>

            <br />

            <button id="create">Create</button>

            <button id="update">Update</button>

            <button id="delete">Delete</button>

            <button id="reset">Reset</button>

        </div>

        <div class="people">

            <table>

                <caption>People</caption>

                <thead>

                    <tr>

                        <th>Creation/Update Timestamp</th>

                        <th>Person</th>

                    </tr>

                </thead>

            </table>

        </div>

        <div class="error">

        </div>

    </div>

    <div class="error">

    </div>


{% endblock %}

people.html has just two major differences:

  • Line 1 tells Jinja2 that this template inherits from the parent.html template.
  • Lines 7 to 45 create the body of the page. This includes the editing section and an empty table to present the list of people. This is the content inserted in the {% block body %}{% endblock %} section of the parent.html file.

The HTML page generated by parent.html and people.html contains no styling information. Instead, the page is rendered in the default style of whatever browser you use to view it. Here’s what your app looks like when rendered by the Chrome browser:

It doesn’t look much like a Single-Page Application! Let’s see what you can do about that.

People CSS

To style the People SPA, you first need to add the normalize.css style sheet. This will make sure that all browsers consistently render elements more closely to HTML 5 standards. The specific CSS for the People SPA is supplied by two style sheets:

  1. parent.css, which you pull in with parent.html
  2. people.css, which you pull in with people.html

You can get the code for these stylesheets at the link below:

You’ll add both normalize.css and parent.css to the <head>...</head> section of parent.html:

<head>

    <meta charset="UTF-8">

    {% block head %}

    <title>{% block title %}{% endblock %} Page</title>

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.0/normalize.min.css">

    <link rel="stylesheet" href="/static/css/parent.css">

    {% endblock %}

</head>

Here’s what these new lines do:

  • Line 5 gets normalize.css from a content delivery network (CDN), so you don’t have to download it yourself.
  • Line 6 gets parent.css from your app’s static folder.

For the most part, parent.css sets the styles for the navigation and error elements. It also changes the default font to Google’s Roboto font using these lines:

@import url(http://fonts.googleapis.com/css?family=Roboto:400,300,500,700);


body, .ui-btn {

    font-family: Roboto;

}

You pull in the Roboto font from a Google CDN. Then, you apply this font to all elements in the SPA body that also have a class of .ui-btn.

Likewise, people.css contains styling information specific to the HTML elements that create the People SPA. You add people.css to the people.html file inside the Jinja2 {% block head %} section:

{% block head %}

    {{ super() }}

    <link rel="stylesheet" href="/static/css/people.css">

{% endblock %}

The file contains a few new lines:

  • Line 2 has a call to {{ super() }}. This tells Jinja2 to include anything that exists in the {% block head %} section of parent.html.
  • Line 3 pulls in the people.css file from your app’s static folder.

After you include the stylesheets, your People SPA will look more like this:

The People SPA is looking better, but it’s still incomplete. Where are the rows of people data in the table? All the buttons in the editor section are enabled, so why don’t they do anything? You’ll fix these issues in the next section with some JavaScript.

People JavaScript

You’ll pull JavaScript files into the People SPA just like you did with the CSS files. You’ll add the following bit of code to the bottom of people.html file:

{% block javascript %}

{{ super() }}

<script type="module">

    // Give the imported MVC components a namespace

    import * as MVC from "/static/js/people.js";


    // Create an intentional global variable referencing the import

    window.mvc = MVC;

</script>

{% endblock %}

Notice the type="module" declaration on the opening <script> tag in line 50. This tells the system that the script is a JavaScript module. The ES6 import syntax will be used to pull the exported parts of the code into the browser context.

The People MVC

All of the SPA pages use a variation of the MVC pattern. Here’s an example implementation in JavaScript:

// Create the MVC components

const model = new Model();

const view = new View();

const controller = new Controller(model, view);


// Export the MVC components as the default

export default {

    model,

    view,

    controller

};

This code doesn’t do anything just yet, but you can use it to see the following elements of MVC structure and implementation:

  • Line 2 creates an instance of the Model class and assigns it to model.
  • Line 3 creates an instance of the View class and assigns it to view.
  • Line 4 creates an instance of the Controller class and assigns it to controller. Note that you pass both model and view to the constructor. This is how the controller gets a link to the model and view instance variables.
  • Lines 7 to 11 export a JavaScript literal object as the default export.

Because you pull in people.js at the bottom of people.html, the JavaScript is executed after your browser creates the SPA DOM elements. This means that JavaScript can safely access the elements on the page and begin to interact with the DOM.

Again, the code above doesn’t do anything just yet. To make it work, you’ll need to define your Model, View, and Controller.

People Model

The Model is responsible for communicating with the REST API provided by the Flask server. Any data that comes from the database, and any data the SPA changes or creates, must go through the Model. All communication with the REST API is done using HTTP AJAX calls initiated by JavaScript.

Modern JavaScript provides fetch(), which you can use to make AJAX calls. The code for your Model class implements one AJAX method to read the REST API URL endpoint /api/people and get all the people in the database:

class Model {

    async read() {

        let options = {

            method: "GET",

            cache: "no-cache",

            headers: {

                "Content-Type": "application/json"

                "accepts": "application/json"

            }

        };

        // Call the REST endpoint and wait for data

        let response = await fetch(`/api/people`, options);

        let data = await response.json();

        return data;

    }

}

Here’s how this code works:

  • Line 1 defines the class Model. This is what will be exported later as part of the mvc object.

  • Line 2 begins the definition of an asynchronous method called read(). The async keyword in front of read() tells JavaScript that this method performs asynchronous work.

  • Lines 3 to 9 create an options object with parameters for the HTTP call, like the method and what the call expects for data.

  • Line 12 uses fetch() to make an asynchronous HTTP call to the /api/people URL REST endpoint provided by the server. The keyword await in front of fetch() tells JavaScript to wait asynchronously for the call to complete. When this is finished, the results are assigned to response.

  • Line 13 asynchronously converts the JSON string in the response to a JavaScript object and assigns it to data.

  • Line 14 returns the data to the caller.

Essentially, this code tells JavaScript to make a GET HTTP request to /api/people, and that the caller is expecting a Content-Type of application/json and json data. Recall that a GET HTTP call equates to Read in a CRUD-oriented system.

Based on the Connexion configuration defined in swagger.yml, this HTTP call will call def read_all(). This function is defined in people.py and queries the SQLite database to build a list of people to return to the caller. You can get the code for all of these files at the link below:

In the browser, JavaScript executes in a single thread and is intended to respond to user actions. Because of this, it’s a bad idea to block JavaScript execution that’s waiting for something to complete, like an HTTP request to a server.

What if the request went out across a very slow network, or the server itself was down and would never respond? If JavaScript were to block and wait for the HTTP request to complete in these kinds of conditions, then it might finish in seconds, minutes, or perhaps not at all. While JavaScript is blocked, nothing else in the browser would react to user actions!

To prevent this blocking behavior, HTTP requests are executed asynchronously. This means that an HTTP request returns to the event loop immediately before the request completes. The event loop exists in any JavaScript application that runs in the browser. The loop continuously waits for an event to complete so it can run the code associated with that event.

When you place the await keyword before fetch(), you tell the event loop where to return when the HTTP request completes. At that point, the request is complete and any data returned by the call is assigned to response. Then, controller calls this.model.read() to receive the data returned by the method. This creates a weak link with the controller, as the model doesn’t know anything about what called it, just what it returned to that caller.

People View

this.view is responsible for interacting with the DOM, which is shown by the display. It can change, add, and delete items from the DOM, which are then re-rendered to the display. The controller makes calls to the view’s methods to update the display. The View is another JavaScript class with methods the controller can call.

Below is a slightly simplified version of the People SPA’s View class:

class View {

    constructor() {

        this.table = document.querySelector(".people table");

        this.person_id = document.getElementById("person_id");

        this.fname = document.getElementById("fname");

        this.lname = document.getElementById("lname");

    }


    reset() {

        this.person_id.textContent = "";

        this.lname.value = "";

        this.fname.value = "";

        this.fname.focus();

    }


    buildTable(people) {

        let tbody,

            html = "";


        // Iterate over the people and build the table

        people.forEach((person) => {

            html += `

            <tr data-person_id="${person.person_id}" data-fname="${person.fname}" data-lname="${person.lname}">

                <td class="timestamp">${person.timestamp}</td>

                <td class="name">${person.fname} ${person.lname}</td>

            </tr>`;

        });

        // Is there currently a tbody in the table?

        if (this.table.tBodies.length !== 0) {

            this.table.removeChild(this.table.getElementsByTagName("tbody")[0]);

        }

        // Update tbody with our new content

        tbody = this.table.createTBody();

        tbody.innerHTML = html;

    }

}

Here’s how this code works:

  • Line 1 begins the class definition.

  • Lines 2 to 7 define the class constructor, much like the def __init__(self): definition in a Python class. The constructor is getting elements from the DOM and creating alias variables to use in other parts of the class. The this. in front of those variable names is much like self. in Python. It designates the current instance of the class when used.

  • Lines 9 to 14 define reset(), which you’ll use to set the page back to a default state.

  • Lines 16 to 36 define buildTable(), which builds the table of people based on the people data passed to it.

The alias variables are created to cache the DOM objects returned by calls to document.getElementByID() and document.querySelector(), which are relatively expensive JavaScript operations. This allows quick use of the variables in the other methods of the class.

Let’s take a closer look at build_table(), which is the second method in the View class:

buildTable(people) {

    let tbody,

        html = "";


    // Iterate over the people and build the table

    people.forEach((person) => {

        html += `

        <tr data-person_id="${person.person_id}" data-fname="${person.fname}" data-lname="${person.lname}">

            <td class="timestamp">${person.timestamp}</td>

            <td class="name">${person.fname} ${person.lname}</td>

        </tr>`;

    });

    // Is there currently a tbody in the table?

    if (this.table.tBodies.length !== 0) {

        this.table.removeChild(this.table.getElementsByTagName("tbody")[0]);

    }

    // Update tbody with our new content

    tbody = this.table.createTBody();

    tbody.innerHTML = html;

}

Here’s how the function works:

  • Line 16 creates the method and passes the people variable as a parameter.
  • Lines 21 to 27 iterate over the people data using JavaScript arrow functions to create a function that builds up the table rows in the html variable.
  • Lines 29 to 31 remove any <tbody> elements in the table if they exist.
  • Line 33 creates a new tbody element in the table.
  • Line 34 inserts the html string previously created into the tbody element as HTML.

This function dynamically builds the table in the People SPA from the data passed to it, which is the list of people that came from the /api/people/ REST API call. This data is used along with JavaScript template strings to generate the table rows to insert into the table.

People Controller

The Controller is the central clearinghouse of the MVC implementation, as it coordinates the activity of both model and view. As such, the code to define it is a little more complicated. Here’s a simplified version:

class Controller {

    constructor(model, view) {

        this.model = model;

        this.view = view;


        this.initialize();

    }

    async initialize() {

        await this.initializeTable();

    }

    async initializeTable() {

        try {

            let urlPersonId = parseInt(document.getElementById("url_person_id").value),

                people = await this.model.read();


            this.view.buildTable(people);


            // Did we navigate here with a person selected?

            if (urlPersonId) {

                let person = await this.model.readOne(urlPersonId);

                this.view.updateEditor(person);

                this.view.setButtonState(this.view.EXISTING_NOTE);


            // Otherwise, nope, so leave the editor blank

            } else {

                this.view.reset();

                this.view.setButtonState(this.view.NEW_NOTE);

            }

            this.initializeTableEvents();

        } catch (err) {

            this.view.errorMessage(err);

        }

    }

    initializeCreateEvent() {

        document.getElementById("create").addEventListener("click", async (evt) => {

            let fname = document.getElementById("fname").value,

                lname = document.getElementById("lname").value;


            evt.preventDefault();

            try {

                await this.model.create({

                    fname: fname,

                    lname: lname

                });

                await this.initializeTable();

            } catch(err) {

                this.view.errorMessage(err);

            }

        });

    }

}

Here’s how it works:

  • Line 1 begins the definition of the Controller class.

  • Lines 2 to 7 define the class constructor and create the instance variables this.model and this.view with their respective parameters. It also calls this.initialize() to set up the event handling and build the initial table of people.

  • Lines 8 to 10 define initialize() and mark it as an asynchronous method. It calls this.initializeTable() asynchronously and waits for it to complete. This simplified version only includes this one call, but the full version of the code contains other initialization methods used for the rest of the event handling set up.

  • Line 11 defines initializeTable() as an asynchronous method. This is necessary because it calls model.read(), which is also asynchronous.

  • Line 13 declares and initializes the urlPersonId variable with the value of the HTML hidden input url_person_id.

  • Line 14 calls this.model.read() and asynchronously waits for it to return with people data.

  • Line 16 calls this.view.buildTable(people) to fill the HTML table with people data.

  • Lines 19 to 28 determine how to update the editor portion of the page.

  • Line 29 calls this.initializeTableEvents() to install the event handling for the HTML table.

  • Line 31 calls this.view.errorMessage(err) to display errors should they occur.

  • Lines 34 to 49 install a click event handler on the create button. This calls this.model.create(...) to create a new person using the REST API, and updates the HTML table with new data.

The bulk of the controller code is like this, setting event handlers for all the expected events on the People SPA page. The controller continues creating functions in those event handlers to orchestrate calls to this.model and this.view, so that they perform the right actions when those events occur.

When your code is complete, your People SPA page will look like this:

The content, styling, and functionality are all complete!

Conclusion

You’ve covered a great deal of new ground and should be proud of what you’ve learned! It can be tricky to jump back and forth between Python and JavaScript to create a complete Single-Page Application.

If you keep your content (HTML), presentation (CSS), and interaction (JavaScript) separate, then you can substantially reduce the complexity. You can also make JavaScript coding more manageable by using the MVC pattern to further break down the complexity of user interaction.

You’ve seen how using these tools and ideas can help you create reasonably complex Single-Page Applications. Now you’re better equipped to make decisions about whether to build an app this way, or take the plunge into a larger framework!

What is REST API? | Restful Web Service

What is REST API? | Restful Web Service

In this post "Restful Web Service", you'll learn: What is Web services, what is API, What is REST API, How REST works and Implementation of REST API

What is REST API? | Restful Web Service

A REST API defines a set of functions to process requests and responses via HTTP protocol.

REST is used in mobile application as well as in web applications.


Build Face Recognition as a REST API on Linux servers using Python Flask

Build Face Recognition as a REST API on Linux servers using Python Flask

There are many open-source face recognition packages like face recognition which you can easily install on Linux servers. This post will demonstrate how to build a RESTful API for face recognition on Linux servers using Python Flask.

With 5G coming, it will take only 0.01 second to upload a 100KB image at a speed of about 100Mbps, so we can deploy almost everything including face recognition as a service on server-side and a light app on client-side. This post will demonstrate how to build a RESTful API for face recognition on Linux servers using Python Flask.

Face_recognition Project

Face recognition is an awesome open source project for face recognition based on dlib, just as described by itself:

The world’s simplest facial recognition api for Python and the command line

With a few lines of Python code, you can compare faces, detect faces and find facial features.

For example, run the sample find_facial_features_in_picture.py (only 40 lines of code) for an Obama image, you can get all facial features drawn as below.

To install it on Linux servers, you can follow the steps in the face_recognition project on github, or just simply download the pre-configured VM.

Build REST API

Let’s define two APIs using the face_recognition package.

  • Compare two faces: Upload two images and return True / False for matching
  • Recognize a face from a known dataset: Upload an image and return the name of the person.

Face Recognition Functions

Two face recognition functions are defined for the two APIs as util in file face_util.py.

The first function compare_faces compares two image files and returns True if the faces in the two images are the same person.

import face_recognition as fr

def compare_faces(file1, file2):
    # Load the jpg files into numpy arrays
    image1 = fr.load_image_file(file1)
    image2 = fr.load_image_file(file2)
    
    # Get the face encodings for 1st face in each image file
    image1_encoding = fr.face_encodings(image1)[0]
    image2_encoding = fr.face_encodings(image2)[0]
    
    # Compare faces and return True / False
    results = fr.compare_faces([image1_encoding], image2_encoding)    
    return results[0]     

The second function face_rec check if the face in the image file is a known face in the dataset, and return the name of the person. There are two known faces in this example.

import face_recognition as fr

# Each face is tuple of (Name,sample image)    
known_faces = [('Obama','sample_images/obama.jpg'),
               ('Peter','sample_images/peter.jpg'),
              ]
    
def face_rec(file):
    """
    Return name for a known face, otherwise return 'Uknown'.
    """
    for name, known_file in known_faces:
        if compare_faces(known_file,file):
            return name
    return 'Unknown' 

REST API with Flask

With the above functions, we can easily define REST API with Flask as follows.

First API is face_match. It takes two image files from form data of a POST request, and calls compare_faces to check if they are a match, then returns a JSON format result.

from flask import Flask, request
import json
from face_util import compare_faces, face_rec

app = Flask(__name__)

@app.route('/face_match')
def face_match():
    if request.method == 'POST':
        # check if the post request has the file part
        if ('file1' in request.files) and ('file2' in request.files):        
            file1 = request.files.get('file1')
            file2 = request.files.get('file2')                         
            ret = compare_faces(file1, file2)     
            resp_data = {"match": bool(ret)} # convert numpy._bool to bool for json.dumps
            return json.dumps(resp_data)         
    
# When debug = True, code is reloaded on the fly while saved
app.run(host='0.0.0.0', port='5001', debug=True)

The second API is face_rec, whichtakes one image file as input and calls face_rec to check if it is a known face, then returns the name of the person in JSON format.

@app.route('/face_rec')
def face_recognition():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' in request.files:
            file = request.files.get('file')                          
            name = face_rec(file)    
            resp_data = {'name': name }
            return json.dumps(resp_data)

You can download the full flask_server_v1.py file here.

Install Flask module by pip install -U flask, then run python flask_server_v1.py to start the server API. The start-up output looks like below.

* Serving Flask app "flask_server_v1" (lazy loading)
...
 * Debug mode: on
 * Running on http://0.0.0.0:5001/ (Press CTRL+C to quit)

And you access these two API with below URL in POST form data format:

  • Compare two faces: http://: 5001/face_match
  • Recognize a face: http://: 5001/face_rec
REST API Client Example

You can call the API using any programming languages. I just take Python as an example. It requires requests module which you can install by pip install -U requests.

In the first example, we call face_match API with two images of the same person.

import requests
import json

def test_face_match():
    url = 'http://127.0.0.1:5001/face_match'
    # open file in binary mode
    files = {'file1': open('sample_images/peter.jpg', 'rb'),
             'file2': open('sample_images/peter2.jpg', 'rb')}     
    resp = requests.post(url, files=files)
    print( 'face_match response:\n', json.dumps(resp.json()) )
    
if __name__ == '__main__':
    test_face_match()

Run the example by python demo_client_part1.py and you will get a response as follows:

{"match": true}

In the second example, we call face_rec API to find the name of a person.

import requests
import json
    
def test_face_rec():
    url = 'http://127.0.0.1:5001/face_rec'
    # open file in binary mode
    files = {'file': open('sample_images/peter2.jpg', 'rb')} 
    resp = requests.post(url, files = files)
    print( 'face_rec response:\n', json.dumps(resp.json()) )
    
if __name__ == '__main__':
    test_face_rec()

Run the script and you will get a response like below:

{"name": "Peter"}
Further Work

You can add more features to the API, for instance, to return face locations and facial features so that you can implement the same things on mobile devices as you can do on Linux servers.

I implemented a version to draw face location and facial features using REST API and you can download here if interested.

Thanks for reading to this end.