Flask & RESTful services

@marjaimate - PyLadies Apr 2016

We help forging your software, product and company

Assumptions

  • You heard about Python

  • You know what a web service is

  • You don't know anything about Flask
  • You don't know what a RESTful service is

Python Web Frameworks

Django, Web2Py, Pylons all known fully fledged frameworks.

 

There are a lot more than these standout options. You can also write your own if you have spare time!

Flask - Micro framework

It's a lightweight and extensible framework for building web applications, let it be backend APIs, blogs, micro services, etc.

Flask - Micro framework

# pies.py

from flask import Flask
app = Flask(__name__)

@app.route("/", methods=["GET"])
def pies():
    return "cherry, steak & kidney, fisherman's"

@app.route('/pies/<pie>', methods=["GET"])
def get_pie(pie):
    return 'Pie: %s' % pie

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

Flask - Micro framework

Supports packages / plugins for:

  • Various database adapters (SQLAlchemy, CouchDB, etc.)
  • Templating languages (DTL, WTForms)
  • RPC-like APIs (XML-RPC, JSON-RPC)
  • Themes
  • Uploads
  • ...

Flask - Routing

@app.route('/user/<username>')
def show_user_profile(username):
    # show the user profile for that user
    return 'User %s' % username

@app.route('/post/<int:post_id>')
def show_post(post_id):
    # show the post with the given id, the id is an integer
    return 'Post %d' % post_id

# Using HTTP verbs
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        do_the_login()
    else:
        show_the_login_form()

Flask - Request

from flask import request

# ...

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
        if valid_login(request.form['username'],
                       request.form['password']):
            return log_the_user_in(request.form['username'])
        else:
            error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.html', error=error)

Flask - Error handling

# 401 unauthorized
@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

# 404 not found
from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

RESTful services

What is REST?

Representational State

Transfer

What is REST?

  • Using HTTP verbs: GET, POST, PUT, PATCH, DELETE, etc.
  • Each action corresponds to a CRUD action
  • A (mostly) deterministic interface between web systems

REST example

HTTP Status URI Description
GET /cats List the herd of cats
POST /cats Create a new cat, add to the collection
GET /cats/:name Retrieve a cat's details
PUT / PATCH /cats/:name Update or create a cat's details
DELETE /cats/:name Delete the cat :(

Flask-RESTful

Flask-RESTful is an extension for Flask that adds support for quickly building REST APIs. 

 

Initially built by Twilio

Flask-RESTful

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

app = Flask(__name__)
api = Api(app)

cats = {}

class CatSimple(Resource):
    def get(self, cat_id):
        return {cat_id: cats[cat_id]}

    def put(self, cat_id):
        cats[cat_id] = request.form['data']
        return {cat_id: cats[cat_id]}

api.add_resource(CatSimple, '/<string:cat_id>')

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

Arguments parsing

from flask_restful import reqparse

parser = reqparse.RequestParser()
parser.add_argument('rate', type=int, help='Rate to charge for this resource')
args = parser.parse_args()

# Required arguments
parser.add_argument('name', required=True, help="Name cannot be blank!")

# Param rewrite
parser.add_argument('name', dest='public_name')

# If an argument fails to pass validation, Flask-RESTful will respond
# with a 400 Bad Request and a response highlighting the error.

# Strict mode is optional
args = parser.parse_args(strict=True)

Routing your resources

# Mount resource routes to the api object
api.add_resource(TodoSimple, '/<string:todo_id>')

# Multiple routes allowed for the same resource
api.add_resource(
    CatList,
    '/',
    '/cats'
)

# Use the routes for dependency injection
class TodoNext(Resource):
    def __init__(**kwargs):
        # smart_engine is a black box dependency
        self.smart_engine = kwargs['smart_engine']
# ... 

smart_engine = SmartEngine()

api.add_resource(TodoNext, '/next', resource_class_kwargs={ 'smart_engine': smart_engine })

Let's do some coding

Flask & REST

By Máté Marjai

Flask & REST

Creating Flask and RESTful services - talk for PyLadies Dublin Apr 2016

  • 1,025