API

Base

class flask_occam.plugin.Occam(app=None, db=None)

Flask extension class for module, which sets up all flask-related capabilities provided by the module. This object can be initialized directly:

from flask import Flask
from flask_occam import Occam

app = Flask(__name__)
occam = Occam(app)

Or lazily via factory pattern:

occam = Occam()
app = Flask(__name__)
occam.init_app(app)

For additional functionality, it is recommended that the extension be linked to the Flask-SQLAlchemy extension tied to the application:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()
occam = Occam(db)
app = Flask(__name__)
db.init_app(app)
occam.init_app(app)
init_app(app, db=None)

Initialize application via lazy factory pattern.

Args:
app (Flask): Flask application. db (SQAlchemy): Flask SQLAlchemy extension.
init_db(db)

Initialize database model extensions.

Args:
db (SQAlchemy): Flask SQLAlchemy extension.
class flask_occam.plugin.DataLoader(model=None)

Helper for loading data into application via config file. Using the following model definition as an example:

class Item(db.Model):
    __tablename__ = 'item'

    # basic
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True, index=True)
    archived = db.Column(db.Boolean, default=False)

You can seed data from the following config file:

- name: item 1
  archived: True

- name: item 2
  archived: False

Into the application using:

# via model directly
User.seed('config.yml')

# via db
db.seed.users('config.yml')

Additionally, this class supports defining multiple types of models in config files. If you want to load multiple types of models via config, you can use the following syntax in your config:

Item:
    - name: item 1
      archived: True

User:
    - name: test user
      email: email@email.com

And load the data in your application via:

db.seed('config.yml')

Databse Mixins

class flask_occam.mixins.ModelMixin

Example database mixin to be used in extension.

classmethod all(limit=None, offset=0)

Return all data with specified limit and offset

commit()

Commit change using session and return item.

classmethod count()

Return total number of items in database.

classmethod create(*args, **kwargs)

Create new record using specified arguments.

delete()

Delete current model object.

classmethod find(limit=None, offset=0, **filters)

Search database with specified limit, offset, and filter criteria.

classmethod get(*args, **filters)

Get single item using filter_by query.

json()

Return dictionary with model properties. This method should be overriden by models to account for model-specific nuances in what to include in return payloads.

classmethod load(data, action=None)

Helper for loading data into application via config file. Using the following model definition as an example:

class Item(db.Model):
    __tablename__ = 'item'

    # basic
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False, unique=True, index=True)
    archived = db.Column(db.Boolean, default=False)

You can seed data from the following config file:

- name: item 1
  archived: True

- name: item 2
  archived: False

Into the application using:

# via model directly
User.seed('config.yml')

# via db
db.seed.users('config.yml')
Arguments:

data (str): File handle or path to config file. action (callable): Function to call on each loaded item.

Takes single created item as input.
update(*args, **kwargs)

Update current item with specified data.

classmethod upsert(*args, **kwargs)

Upsert specified data into database. If the data doesn’t exist in the database, it will be created, otherwise, the record will be updated. This method automatically detects unique keys by which to query the database for existing records.

Note

The performance of this could be improved by doing bulk operations for querying and the create/update process.

Handlers

class flask_occam.handlers.ActionHandler

Handler for urls like /model/:action, where <action> represents a POST request. This allows developers to define individual methods for performing model-specific actions instead of needing to manage that complexity in the get/put/post methods.

post(**kwargs)

Overwrite post endpoint for handler.

Args:
ident (int): Identifier for model. action (str): String for action to perform.
class flask_occam.handlers.QueryHandler

Handler for urls like /model/:query, where <query> represents a GET request. This allows developers to define individual methods for performing model-specific queries instead of needing to manage that complexity in the get/put/post methods.

get(**kwargs)

Overwrite get endpoint for handler.

Args:
ident (int): Identifier for model. query (str): String for query to perform.

Converters

class flask_occam.converters.ModelConverter(map, model)

For url inputs containing a model identifier, look up the model and return the object.

This method simplifies a lot of the boilerplate needed to do model look ups in REST apis.

Examples:

@app.route('/users/<id(User):user>')
def get_user(user):
    return jsonify(user.json())

In addition, this class can be inherited and used for other custom parameter url converters. For instance, here is how you might use it to create a name converter:

class NameConverter(ModelConverter):
    __param__ = 'name'

app.url_map.converters['name'] = NameConverter

# ... handlers ...

@app.route('/users/<name(User):user>')
def get_user(user):
    return jsonify(user.json())

Decorators

class flask_occam.decorators.log(msg, level=None)

Decorator for adding default logging functionality to api methods.

classmethod critical(msg)

Log provided message with level CRITICAL.

classmethod debug(msg)

Log provided message with level DEBUG.

classmethod error(msg)

Log provided message with level ERROR.

classmethod info(msg)

Log provided message with level INFO.

classmethod warning(msg)

Log provided message with level WARNING.

flask_occam.decorators.paginate(**options)

Automatically force request pagination for endpoints that shouldn’t return all items in the database directly. If this decorator is used, limit and offset request arguments are automatically included in the request. The burden is then on developers to do something with those limit and offset arguments. An example request header set by this decorator is as follows:

Link: <https://localhost/items?limit=50&offset=50>; rel="next",
      <https://localhost/items?limit=50&offset=500>; rel="last"
Args:

limit (int): Number of entries to limit a query by. total (int, callable): Number or callable for determining

the total number of records that can be returned for the request. This is used in determining the pagination header.
flask_occam.decorators.transactional(func)

Decorator for wrapping transactional support around a request handler or API method. By wrapping a method in @transactional, any exception thrown will force a session rollback. Otherwise, the session will be committed.

class flask_occam.decorators.validate(*args, **kwargs)

Validate payload data inputted to request handler or API method. This decorator can take any arbitrary data structure and determine rules to validate inputs with. It can be run within the context of a flask request, or for an api method directly. See the examples below for details.

Note

In a request context, a ValidationError will be thrown (HTTP 422 Unprocessable Entity). Outside of a request, a ValueError will be raised.

Examples:

Using types with API method:

@validate(
    email=str,
    password=str
)
def login(email, password):
    return

Using nested types (mixing types and validators):

from wtforms import validators

@blueprint.route('/login', methods=['POST'])
@validate(
    data=dict(
        email=validators.Email(),
        password=str
    )
)
def login():
    return

With pre-defined validation object:

from wtforms import validators, Form, BooleanField
from wtforms import StringField, PasswordField
class LoginValidator(Form):
    email = StringField('Email Address', [
        validators.DataRequired(),
        validators.Email(),
    ])
    password = PasswordField('Password', [
        validators.DataRequired(),
        validators.Length(min=4, max=25)
    ])

@blueprint.route('/login', methods=['POST'])
@validate(LoginForm)
def login():
    return

Logic specified into decorator:

@blueprint.route('/login', methods=['POST'])
@validate(
    email = StringField('Email Address', [
        validators.DataRequired(),
        validators.Email(),
    ])
    password = PasswordField('Password', [
        validators.DataRequired(),
        validators.Length(min=4, max=25)
    ])
)
def login():
    return
classmethod optional(*args, **kwargs)

Force all specified parameters to be wrapped with optional() declaration.