Quickstart

This page is intended to help get you off and running with Drowsy as quickly as possible. To do that, we’ll be walking through the creation of an example API using the Chinook Sqlite database that comes bundled with Drowsy’s tests, and Flask as our web framework. Note that Drowsy is web framework agnostic, and that Flask is by no means a requirement.

Some familiarity with SQLAlchemy and Marshmallow is assumed prior to working with Drowsy.

A working version of all of the below code can be found in the examples that come bundled in the Drowsy distribution, under the chinook_api folder.

Defining Models

The first thing you’ll need to do is define your SQLAlchemy models. Hopefully you’re familiar with SQLAlchemy already, but as a quick example, we’ll define a few models here:

from sqlalchemy import Column, ForeignKey, Integer, Unicode, orm
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.schema import ForeignKeyConstraint


Base = declarative_base()
metadata = Base.metadata


class Album(Base):

    """SQLAlchemy model for the Album table in our database."""

    __tablename__ = 'Album'

    album_id = Column("AlbumId", Integer, primary_key=True)
    title = Column("Title", Unicode(160), nullable=False)
    artist_id = Column(
        "ArtistId", ForeignKey('Artist.ArtistId'),
        nullable=False, index=True)

    artist = orm.relationship('Artist', backref="albums")


class Artist(Base):

    """SQLAlchemy model for the Artist table in our database."""

    __tablename__ = 'Artist'

    artist_id = Column("ArtistId", Integer, primary_key=True)
    name = Column("Name", Unicode(120)

A complete set of example model definitions can be seen in models.py.

Defining Schemas

Once you’ve defined some models, the next step is to define some schemas. Schemas handle a number of important tasks, including serializing data from model instances, and validating and deserializing user input into model instances.

Drowsy provides the ModelResourceSchema class as the main building block to creating schemas around your models.

from drowsy.convert import ModelResourceConverter
from drowsy.schema import ModelResourceSchema
from chinook_api.models import (
    Album, Artist, CompositeOne, CompositeMany, CompositeNode,
    Customer, Employee, Genre, Invoice, InvoiceLine, MediaType,
    Node, Playlist, Track
)


class AlbumSchema(ModelResourceSchema):
    class Meta:
        model = Album
        model_converter = ModelResourceConverter


class ArtistSchema(ModelResourceSchema):
    class Meta:
        model = Artist
        model_converter = ModelResourceConverter

Here we’re using a ModelResourceConverter to do a lot of work behind the scenes for us. The job of the converter is to take fields from the specified model and turn them into fields for the schema. By default, fields are named by using the attr names of the SQLAlchemy model.

If we were to use CamelModelResourceConverter rather than the default ModelResourceConverter, it would convert field names like album_id to albumId, to make the serialized result conform to standard JSON naming convention.

There’s a good chance you’ll want to extend one of the included converters and create your own, as currently they include child resources by default and are very permissive. Also note that you can always explicitly define schema fields individually as you would in any other Marshmallow schema, if using one of the converters feels too much like magic for your taste.

A complete set of example resource definitions can be seen in schemas.py.

Defining Resources

After defining some schemas, the next step is to define our resources. Resources utilize schemas to perform CRUD style operations, and help manage any nested resource embedding in API query results.

from drowsy.resource import ModelResource
from chinook_api.schemas import AlbumSchema, ArtistSchema


class AlbumResource(ModelResource):
    class Meta:
        schema_cls = AlbumSchema


class ArtistResource(ModelResource):
    class Meta:
        schema_cls = ArtistSchema

A complete set of example resource definitions can be seen in resources.py.

Routing

The bulk of work is now done, and now we just need to tie it all together. We’ll be using Flask here, but again, the same principals will apply in any other Python web framework.

To put everything together, we’ll create a single endpoint that relies on a ModelResourceRouter to use the URL and determine which ModelResource the request should go to. The router also makes use of ModelQueryParamParser to parse out user supplied filters, embeds, and more from query parameters.

import os
import json
from flask import Flask, g, request, Response
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker, scoped_session
from drowsy.exc import (
    UnprocessableEntityError, BadRequestError, MethodNotAllowedError,
    ResourceNotFoundError
)
from drowsy.router import ModelResourceRouter
from .models import *
from .schemas import *
from .resources import *


app = Flask(__name__)


# Set up SQLAlchemy session factory
# You'll want to do this more robustly in a real app.
DB_PATH = os.path.join(
    os.path.dirname(os.path.abspath(__file__)),
    "chinook.sqlite")
DB_ENGINE = create_engine("sqlite+pysqlite:///" + DB_PATH)

@app.before_request
def prepare_db_session():
    """Prepare a database session and attach it to Flask.g"""
    g.db_session = scoped_session(sessionmaker(bind=DB_ENGINE))


@app.teardown_request
def end_db_session(error):
    """Commit any changes or rollback on failure."""
    if hasattr(g, "db_session"):
        db_session = g.db_session
        try:
            if error:
                raise error
            db_session.commit()
        except SQLAlchemyError:
            db_session.rollback()
        finally:
            db_session.remove()


@app.route(
    "/api/<path:path>",
    methods=["GET", "POST", "PATCH", "PUT", "DELETE", "HEAD", "OPTIONS"])
def api_router(path):
    """Generic API router.

    You'll probably want to be more specific with your routing.

    We're using the ModelResourceRouter, which automatically routes
    based on the class name of each Resource, and handles nested
    routing, querying, and updating automatically.

    """
    # get your SQLAlchemy db session however you normally would
    db_session = g.db_session
    # query params are used to parse fields to include, embeds,
    # sorts, and filters.
    router = ModelResourceRouter(session=db_session)
    query_params = request.values.to_dict()
    errors = None
    message = None
    code = None
    status = 200
    try:
        if request.method.lower() == "POST":
            status = 201
        result = router.dispatcher(
            request.method,
            path,
            query_params=query_params,
            data=request.json)
        if result is None:
            status = 204
        else:
            result = json.dumps(result)
        return Response(
            result,
            mimetype="application/json",
            status=status)
    except UnprocessableEntityError as exc:
        status = 433
        errors = exc.errors
        message = exc.message
        code = exc.code
    except MethodNotAllowedError as exc:
        status = 405
        message = exc.message
        code = exc.code
    except BadRequestError as exc:
        status = 400
        message = exc.message
        code = exc.code
    except ResourceNotFoundError as exc:
        status = 404
        message = exc.message
        code = exc.code
        print(code)
    if code is not None or message:
        result = {"message": message, "code": code}
        if errors:
            result["errors"] = errors
        return Response(
            json.dumps(result),
            mimetype="application/json",
            status=status)

if __name__ == '__main__':
    # Never run with debug in production!
    app.run(debug=True)

You can now run this the same way you would any other Flask app and have an incredibly flexible API up and running in front of your database.

Keep in mind that this is a very, very simplistic implementation. Routing is done here using resource class name, such that the resource used for the path /albums is determined by transforming albums to upper camel case (Albums), using the inflection library to remove pluralization (Album), and adding Resource to the end (AlbumResource). If this feels too much like magic for your tastes, and I don’t blame you if so, you can pass the resource you want to use in explicitly to the router’s constructor and have separate routing definitions for each top level resource. You’re also more than welcome to handle routing on your own if the included ModelResourceRouter doesn’t handle your use cases.

A slightly more robust implementation can be seen in the example API project in api.py.

Next Steps

Now that you’ve got an actual API up and running, you can head over to the Querying and Mutations sections to get an overview of how to interact with your new API.

You’ll also want to be sure to check out the Permissions Handling section to gain an understanding of how to properly secure a Drowsy based API.