Flask restless

Preview:

Citation preview

FLASK RESTLESSFAST AND EASY REST API’S

Code SamplesGithub

https://github.com/mandshaw/flask_microbrewery

What is Flask RestlessLibrary built around flask

Model driven

Define your models

Define their relationships

Expose your models

Specify what to expose them on (GET, POST, PUT, DELETE

What is Flask Restless

Uses SQLAlchemy

Supports SQLite, Postgresql, MySQL, Oracle and many more

Built in query language (FOR FREE!)

Models

What is a model?

Comes from SQLAlchemy or Flask-SQLAlchemy

Used to represent objects which will become records in a database

Flask-SQLAlchemy

Abstracts away a lot of unnecessary SQLAlchemy config and makes your life easier.

Read more here

Defining a Model# Define the modelsclass Beer(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode, unique=True)

Will execute:

CREATE TABLE beer (

id INTEGER NOT NULL,

name VARCHAR,

PRIMARY KEY (id),

UNIQUE (name),

)

Turning a Model into a API

# Create the DBdb.create_all()# Create the Flask-Restless API manager.manager = APIManager(app, flask_sqlalchemy_db=db)# Create the API endpoints from the DB Models# These will be available at /api/<tablename>manager.create_api(Beer, methods=['GET', 'POST', 'PUT', 'DELETE'])

DEMO STEP/1

Relationships

Creating a one to many Relationship

Relationship always goes on the ‘one’ side # Define the modelsclass Beer(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode, unique=True) # Create a foreign key type_id = db.Column(db.Integer, db.ForeignKey(‘type.id’)) class Type(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode, unique=True) # Create a relationship beers = db.relationship('Beer', backref=db.backref('type'), lazy=‘dynamic')

DEMO STEP/2

Making Fields Invisible

Excluding Columns

You can use exclude_columns or include_columns to hide columns in the response

manager.create_api(Beer, methods=['GET', 'POST', 'PUT', 'DELETE'], exclude_columns=['reviews', 'type'])

DEMO STEP/3

Pre and Post Processing

Consider

On a POST or PUT for a review you want to add the date that the review was created

Do you?

Do this client side (What time zone? What format)

Do this server side

Processors# Augments the request with the current db DATETIMESTAMP # When submitted def add_current_date(**kw): kw['data']['date'] = 'CURRENT_TIMESTAMP' return kw # Create the API endpoints from the DB Models# These will be available at /api/<tablename> manager.create_api( Review, methods=['GET', 'POST', 'PUT', 'DELETE'], preprocessors = { 'POST': [add_current_date], 'PUT_SINGLE': [add_current_date] })

Processors

Preprocessors -> before the requests is processed

Postprocessors -> before the response is returned

DEMO STEP/4

Validation

Validation

Not performed by Flask-Restless

Roll your own or use a combination of the SQLAlchemy validate decorator and flasks abort

Validationfrom flask import Flask, abort from sqlalchemy.orm import validates

class Review(db.Model): id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime) stars = db.Column(db.Integer) comment = db.Column(db.Unicode) beer_id = db.Column(db.Integer, db.ForeignKey('beer.id')) author_id = db.Column(db.Integer, db.ForeignKey('reviewer.id')) @validates('stars') def validate_name(self, key, stars): if stars >= 1 and stars <=5: return stars else: return abort(400, description='A review must have a star rating of between 1 and 5')

Pagination

Server side Pagination

Natively uses server side pagination (default is 10 results per page)

<host>/api/<resource>?page=2 Give you page 2

Set the results_per_page attribute when creating your api to over-ride

manager.create_api(Reviewer, methods=['GET', 'POST', 'PUT', 'DELETE'], results_per_page=2)

Querying data

NULL is the Chuck Norris of the database. Nothing can be compared to it. - Unknown

Querying Data

Support query params on GETS

Uses Query Filters

<host>/api/<resource>/?q={“filter”:[…filters…]}

Query FiltersLists of

{“name”: name, “op”: op, “val”: val}

{“name”: name, “op”: op, “field”: field}

name The field to use

op The operator to use

val The Value to use

field The field to compare name to

Valid Operatorsequal ==, eq, equals, equals_to

not equal !=, neq, does_not_equal, not_equal_to

comparison >, gt, <, lt, >=, ge, gte, geq, <=, le, lte, leq

membership in, not_in

null check is_null, is_not_null

like like

has has

any any

Querying Data

Extremely feature rich

Order by

Group By

Single result expected

READ THE DOCS

ExamplesGet all the Hipsters

GET localhost:5000/api/reviewer?q={"filters":[{"name":"name","op": "ilike", "val":"Hipster%"}]}

Reviews with more than 3 stars

GET localhost:5000/api/review?q={"filters":[{"name":"stars","op": "gt", "val":3}]}

All reviews made by Hipster Jane

GET localhost:5000/api/review?q={"filters":[{"name":"author","op":"has","val":{"name":"name", "op":"eq", "val":"Hipster Jane”}}]}

Examples

You can also use relationships in a GET

All reviews made by Hipster Jane(id=2)

GET localhost:5000/api/reviewer/2/reviews

Further Reading

You can use SQLAlchemy migrate to migrate you DB revisions

Read all about it (and lots more) here

QUESTIONS?