Start a flask project from zero: Building a REST API

Created:

Introduction

In this tutorial we are going to build an application with flask. We will setup management commands, a sqlalchemy model and an API rest to access and modify the model.

Step 1: Prepare virtualenv and install requirements

We are going to use pyenv + virutalenv plugin. For python package scaffolding we will use cookie cutter. If you want to know how to setup pyenv, please pyenv+virtualenv tutorial. In particular we will use Python 3.

pyenv virtualenv 3.6.0 flask_app
pyenv activate flask_app
pip install cookiecutter
# now create with cookiecutter a python package
cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git

CD into the new directory and open the file requirements_dev.txt and paste the following:

bumpversion==0.5.3
wheel==0.29.0
watchdog==0.8.3
flake8==2.6.0
tox==2.3.1
coverage==4.1
Sphinx==1.4.8
cryptography==1.7
PyYAML==3.11
Flask==0.12.1
Flask-Migrate==2.0.3
Flask-RESTful==0.3.5
Flask-Script==2.0.5
Flask-SQLAlchemy==2.2

In this tutorial we are going to use Flask-SQLAlchemy, Flask-Migrate, Flask-RESTful and Flask-Script.

  • Flask-Script: Provides support for writing external scripts.
  • Flask-SQLAlchemy: Provides support for using sqlalchemy.
  • Flask-Migrate: Allows to add migration to the sqlalchemy model.
  • Flask-RESTful: Add support to quickly build REST APIs.

Step 2: Creating flask skeleton directories and show a Hello World message

We are going to start with two new directories: static and templates.

In the directory where you setup.py was created, execute the follwoing commands:

mkdir -p flask_app/{templates,static,views,scripts}

touch flask_app/scripts/__innit__.py
touch flask_app/scripts/app.py
touch flask_app/scripts/start.py
touch flask_app/scripts/manage.py
touch flask_app/views/__init__.py
touch flask_app/__init__.py
touch flask_app/models.py
touch flask_app/settings.py

On the flask_app/app.py we are going to add the code for app initialization. We will import the views, so flask can add those views.

from flask import Flask
from flask_restful import Api
from flask_app.settings import SQLALCHEMY_DATABASE_URI, SQLALCHEMY_TRACK_MODIFICATIONS

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = SQLALCHEMY_TRACK_MODIFICATIONS
db = SQLAlchemy(app)
api = Api(app)

# we need to import the views and model (which are not used here)
from flask_app import views
from flask_app.models import Animal

Next we will add the index route and show a "Hello, World!" messaga. Add the following content to the views/init.py file:

from flask_app import app

@app.route('/')
@app.route('/index')
def index():
    return "Hello, World!"

Add the following code to the flask_app/scripts/start.py. This code will start the flask application in debug mode

from flask_script import Manager

from flask_app.app import app

manager = Manager(app)

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

Now test your application running the file:

python setup.py develop # else your imports will fail
python scripts/start.py runserver
# open you browser at http://localhost:5000/
# you should see the "Hello, World!" string.

Step 3: Adding the model

In this simple tutorial we are going to show animals and colors form a model. We are not going to add authentication to make this tutorial simple. Here is the model we are going to show and create records. Open the file flask_app/models.py and add the following content:

from flask_app.app import db

class Animal(db.Model):
    __tablename__ = 'animal'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    color = db.Column(db.String(100))

Open the settings.py and add the following:

SQLALCHEMY_DATABASE_URI = 'sqlite:///flask_app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

Change the settings as you need, in this example we will use sqlite. For more information on how to setup the url see sqlalchemy engince docs.

Now we are going to create a manage script for the migrations. Open the file flask_app/scripts/manage.py to append these content:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

from flask_app.app import app, db

migrate = Migrate(app, db)

manager = Manager(app)
manager.add_command('migrate', MigrateCommand)

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

Now initialize the empty database schema using:

python flask_app/scraipts/manage.py migrate init
# the next command will create the sqlite3 file with the database
python flask_app/scraipts/manage.py migrate migrate
# to apply the migration to the database
python flask_app/scraipts/manage.py migrate upgrade

This will create a directory called migrations. You need to save this directory in your git in order to have historical model schema changes. At this point we have enable migrations for our model.

Step 4: Adding a REST API

To access the model we just created we are going to use Flask-RESTful.

Open the file views/init.py


from flask import Flask, request
from flask_restful import reqparse, Resource, Api

from nichescore.app import api, db
from nichescore.models import Animal


class AnimalsResource(Resource):
    def get(self, animal_id):
        animal = db.session.query(Animal).filter_by(id=animal_id).first()
        if not animal:
            return {'message': 'Not found'}, 404
        return {animal_id: animal}

    def put(self, animal_id):
        animal = db.query(Animal).filter_by(id=animal_id)
        animal.color = request.form['color']
        db.session.commit()
        return {animal_id: animal}


class AnimalsListResource(Resource):

    def get(self):
        return [{animal.id: {'name': animal.name, 'color': animal.color}} for animal in db.session.query(Animal).all()]

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('name')
        parser.add_argument('color')
        args = parser.parse_args()
        if not 'name' in args or not 'color' in args:
            # we return bad request since we require name and color
            return {'message': 'Missing required parameters.'}, 400
        new_animal = Animal(name=args['name'], color=args['color'])
        db.session.add(new_animal)
        db.session.commit()
        return {new_animal.id: {'name': animal.name, 'color': animal.color}}, 201

api.add_resource(AnimalsResource, 'animal/<animal_id>')
api.add_resource(AnimalsListResource, 'animals')

Now for testing the API we first are going to create a new animal using curl:

curl http://localhost:5000/animals -d "name=test;color=red" -X POST -v

You can get the list with:

curl http://localhost:5000/animals -v

Get one animal by id:

curl http://localhost:5000/animal/1 -v

Edit an animal by id:

curl http://localhost:5000/animal/1 -d "color=green" -X PUT