Flask-Restless

Flask-Restless provides simple generation of ReSTful APIs for database models defined using SQLAlchemy (or Flask-SQLAlchemy). The generated APIs send and receive messages in JSON format.

User’s guide

Downloading and installing Flask-Restless

Flask-Restless can be downloaded from its page on the Python Package Index. The development version can be downloaded from its page at GitHub. However, it is better to install with pip (hopefully in a virtual environment provided by virtualenv):

pip install Flask-Restless

Flask-Restless requires Python version 2.6, 2.7, or 3.3. Python 3.2 is not supported by Flask and therefore cannot be supported by Flask-Restless.

Flask-Restless has the following dependencies (which will be automatically installed if you use pip):

Quickstart

For the restless:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import flask
import flask.ext.sqlalchemy
import flask.ext.restless

# Create the Flask application and the Flask-SQLAlchemy object.
app = flask.Flask(__name__)
app.config['DEBUG'] = True
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)

# Create your Flask-SQLALchemy models as usual but with the following two
# (reasonable) restrictions:
#   1. They must have a primary key column of type sqlalchemy.Integer or
#      type sqlalchemy.Unicode.
#   2. They must have an __init__ method which accepts keyword arguments for
#      all columns (the constructor in flask.ext.sqlalchemy.SQLAlchemy.Model
#      supplies such a method, so you don't need to declare a new one).
class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)


class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    vendor = db.Column(db.Unicode)
    purchase_time = db.Column(db.DateTime)
    owner_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    owner = db.relationship('Person', backref=db.backref('computers',
                                                         lazy='dynamic'))


# Create the database tables.
db.create_all()

# Create the Flask-Restless API manager.
manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)

# Create API endpoints, which will be available at /api/<tablename> by
# default. Allowed HTTP methods can be specified as well.
manager.create_api(Person, methods=['GET', 'POST', 'DELETE'])
manager.create_api(Computer, methods=['GET'])

# start the flask loop
app.run()

You may find this example at examples/quickstart.py in the source distribution; you may also view it online at GitHub.

Further examples can be found in the examples/ directory in the source distribution or on the web.

Creating API endpoints

To use this extension, you must have defined your database models using either SQLAlchemy or Flask-SQLALchemy.

The basic setup for Flask-SQLAlchemy is the same. First, create your flask.Flask object, flask.ext.sqlalchemy.SQLAlchemy object, and model classes as usual but with the following two (reasonable) restrictions on models:

  1. They must have a primary key column of type sqlalchemy.Integer or type sqlalchemy.Unicode.
  2. They must have an __init__ method which accepts keyword arguments for all columns (the constructor in flask.ext.sqlalchemy.SQLAlchemy.Model supplies such a method, so you don’t need to declare a new one).
import flask
import flask.ext.sqlalchemy

app = flask.Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = flask.ext.sqlalchemy.SQLAlchemy(app)

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)
    computers = db.relationship('Computer',
                                backref=db.backref('owner',
                                                   lazy='dynamic'))


class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    vendor = db.Column(db.Unicode)
    owner_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    purchase_time = db.Column(db.DateTime)


db.create_all()

If you are using pure SQLAlchemy:

from flask import Flask
from sqlalchemy import Column, Date, DateTime, Float, Integer, Unicode
from sqlalchemy import ForeignKey
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relationship
from sqlalchemy.orm import scoped_session, sessionmaker

app = Flask(__name__)
engine = create_engine('sqlite:////tmp/testdb.sqlite', convert_unicode=True)
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
mysession = scoped_session(Session)

Base = declarative_base()
Base.metadata.bind = engine

class Computer(Base):
    __tablename__ = 'computer'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode, unique=True)
    vendor = Column(Unicode)
    buy_date = Column(DateTime)
    owner_id = Column(Integer, ForeignKey('person.id'))

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode, unique=True)
    age = Column(Float)
    other = Column(Float)
    birth_date = Column(Date)
    computers = relationship('Computer',
                             backref=backref('owner', lazy='dynamic'))

Base.metadata.create_all()

Warning

Attributes of these entities must not have a name containing two underscores. For example, this class definition is no good:

class Person(db.Model):
    __mysecretfield = db.Column(db.Unicode)

This restriction is necessary because the search feature (see Making search queries) uses double underscores as a separator. This may change in future versions.

Second, instantiate a flask.ext.restless.APIManager object with the Flask and SQLAlchemy objects:

import flask.ext.restless

manager = flask.ext.restless.APIManager(app, flask_sqlalchemy_db=db)

Or if you are using pure SQLAlchemy, specify the session you created above instead:

manager = flask.ext.restless.APIManager(app, session=mysession)

Third, create the API endpoints which will be accessible to web clients:

person_blueprint = manager.create_api(Person,
                                      methods=['GET', 'POST', 'DELETE'])
computer_blueprint = manager.create_api(Computer)

Note that you can specify which HTTP methods are available for each API endpoint. There are several more customization options; for more information, see Customizing the ReSTful interface.

Due to the design of Flask, these APIs must be created before your application handles any requests. The return value of APIManager.create_api() is the blueprint in which the endpoints for the specified database model live. The blueprint has already been registered on the Flask application, so you do not need to register it yourself. It is provided so that you can examine its attributes, but if you don’t need it then just ignore it:

manager.create_api(Person, methods=['GET', 'POST', 'DELETE'])
manager.create_api(Computer)

If you wish to create the blueprint for the API without registering it (for example, if you wish to register it later in your code), use the APIManager.create_api_blueprint() method instead:

blueprint = manager.create_api_blueprint(Person, methods=['GET', 'POST'])
# later...
app.register_blueprint(blueprint)

By default, the API for Person, in the above code samples, will be accessible at http://<host>:<port>/api/person, where the person part of the URL is the value of Person.__tablename__:

>>> import json  # import simplejson as json, if on Python 2.5
>>> import requests  # python-requests is installable from PyPI...
>>> newperson = {'name': u'Lincoln', 'age': 23}
>>> r = requests.post('/api/person', data=json.dumps(newperson),
...                   headers={'content-type': 'application/json'})
>>> r.status_code, r.headers['content-type'], r.data
(201, 'application/json', '{"id": 1}')
>>> newid = json.loads(r.data)['id']
>>> r = requests.get('/api/person/%s' % newid,
...                  headers={'content-type': 'application/json'})
>>> r.status_code, r.headers['content-type']
(200, 'application/json')
>>> r.data
{
  "other": null,
  "name": "Lincoln",
  "birth_date": null,
  "age": 23.0,
  "computers": [],
  "id": 1
}

If the primary key is a Unicode instead of an Integer, the instances will be accessible at URL endpoints like http://<host>:<port>/api/person/foo instead of http://<host>:<port>/api/person/1.

Customizing the ReSTful interface

HTTP methods

By default, the APIManager.create_api() method creates a read-only interface; requests with HTTP methods other than GET will cause a response with 405 Method Not Allowed. To explicitly specify which methods should be allowed for the endpoint, pass a list as the value of keyword argument methods:

apimanager.create_api(Person, methods=['GET', 'POST', 'DELETE'])

This creates an endpoint at /api/person which responds to GET, POST, and DELETE methods, but not to other ones like PUT or PATCH.

The recognized HTTP methods and their semantics are described below (assuming you have created an API for an entity Person). All endpoints which respond with data respond with serialized JSON strings.

GET /api/person

Returns a list of all Person instances.

GET /api/person/(int: id)

Returns a single Person instance with the given id.

GET /api/person?q=<searchjson>

Returns a list of all Person instances which match the search query specified in the query parameter q. For more information on searching, see Making search queries.

DELETE /api/person/(int: id)

Deletes the person with the given id and returns 204 No Content.

POST /api/person

Creates a new person in the database and returns its id. The initial attributes of the Person are read as JSON from the body of the request. For information about the format of this request, see Format of requests and responses.

PATCH /api/person/(int: id)

Updates the attributes of the Person with the given id. The attributes are read as JSON from the body of the request. For information about the format of this request, see Format of requests and responses.

PATCH /api/person

This is only available if the allow_patch_many keyword argument is set to True when calling the create_api() method. For more information, see Enable patching all instances.

Updates the attributes of all Person instances. The attributes are read as JSON from the body of the request. For information about the format of this request, see Format of requests and responses.

PUT /api/person
PUT /api/person/(int: id)

Aliases for PATCH /api/person and PATCH /api/person/(int:id).

API prefix

To create an API at a different prefix, use the url_prefix keyword argument:

apimanager.create_api(Person, url_prefix='/api/v2')

Then your API for Person will be available at /api/v2/person.

Collection name

By default, the name of the collection which appears in the URLs of the API will be the name of the table which backs your model. If your model is a SQLAlchemy model, this will be the value of __tablename__. If your model is a Flask-SQLAlchemy model, this will be the lowercase name of the model with CamelCase changed to camel_case.

To provide a different name for the model, provide a string to the collection_name keyword argument of the APIManager.create_api() method:

apimanager.create_api(Person, collection_name='people')

Then the API will be exposed at /api/people instead of /api/person.

Specifying one of many primary keys

If your model has more than one primary key (one called id and one called username, for example), you should specify the one to use:

manager.create_api(User, primary_key='username')

If you do this, Flask-Restless will create URLs like /api/user/myusername instead of /api/user/137.

Enable patching all instances

By default, a PATCH /api/person request (note the missing ID) will cause a 405 Method Not Allowed response. By setting the allow_patch_many keyword argument of the APIManager.create_api() method to be True, PATCH /api/person requests will patch the provided attributes on all instances of Person:

apimanager.create_api(Person, methods=['PATCH'], allow_patch_many=True)

Capturing validation errors

By default, no validation is performed by Flask-Restless; if you want validation, implement it yourself in your database models. However, by specifying a list of exceptions raised by your backend on validation errors, Flask-Restless will forward messages from raised exceptions to the client in an error response.

A reasonable validation framework you might use for this purpose is SQLAlchemy Validation. You can also use the validates() decorator that comes with SQLAlchemy.

For example, if your validation framework includes an exception called ValidationError, then call the APIManager.create_api() method with the validation_exceptions keyword argument:

from cool_validation_framework import ValidationError
apimanager.create_api(Person, validation_exceptions=[ValidationError])

Note

Currently, Flask-Restless expects that an instance of a specified validation error will have a errors attribute, which is a dictionary mapping field name to error description (note: one error per field). If you have a better, more general solution to this problem, please visit our issue tracker.

Now when you make POST and PATCH requests with invalid fields, the JSON response will look like this:

HTTP/1.1 400 Bad Request

{ "validation_errors":
    {
      "age": "Must be an integer",
    }
}

Currently, Flask-Restless can only forward one exception at a time to the client.

Exposing evaluation of SQL functions

If the allow_functions keyword argument is set to True when creating an API for a model using APIManager.create_api(), then an endpoint will be made available for GET /api/eval/person which responds to requests for evaluation of functions on all instances the model.

For information about the request and response formats for this endpoint, see Function evaluation.

Specifying which columns are provided in responses

By default, all columns of your model will be exposed by the API. If the include_columns keyword argument is an iterable of strings, only columns with those names (that is, the strings represent the names of attributes of the model which are Column objects) will be provided in JSON responses for GET requests.

For example, if your models are defined like this (using Flask-SQLAlchemy):

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)
    computers = db.relationship('Computer')

and you want your JSON responses to include only the values of the name and birth_date columns, create your API with the following arguments:

apimanager.create_api(Person, include_columns=['name', 'birth_date'])

Now requests like GET /api/person/1 will return JSON objects which look like this:

{"name": "Jeffrey", "birth_date": "1999-12-31"}

The exclude_columns keyword argument works similarly; it forces your JSON responses to include only the columns not specified in exclude_columns. For example:

apimanager.create_api(Person, exclude_columns=['name', 'birth_date'])

will produce responses like:

{"id": 1, "computers": [{"id": 1, "vendor": "Apple", "model": "MacBook"}]}

In this example, the Person model has a one-to-many relationship with the Computer model. To specify which columns on the related models will be included or excluded, include a string of the form '<relation>.<column>', where <relation> is the name of the relationship attribute of the model and <column> is the name of the column on the related model which you want to be included or excluded. For example:

includes = ['name', 'birth_date', 'computers', 'computers.vendor']
apimanager.create_api(Person, include_columns=includes)

will produce responses like:

{
  "name": "Jeffrey",
  "birth_date": "1999-12-31",
  "computers": [{"vendor": "Apple"}]
}

An attempt to include a field on a related model without including the relationship field has no effect:

includes = ['name', 'birth_date', 'computers.vendor']
apimanager.create_api(Person, include_columns=includes)
{"name": "Jeffrey", "birth_date": "1999-12-31"}

To include the return value of an arbitrary method defined on a model, use the include_methods keyword argument. This argument must be an iterable of strings representing methods with no arguments (other than self) defined on the model for which the API will be created:

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    name = Column(Unicode)
    age = Column(Integer)

    def name_and_age(self):
        return "%s (aged %d)" % (self.name, self.age)

include_methods = ['name_and_age']
manager.create_api(Person, include_methods=['name_and_age'])

A response to a GET request will then look like this:

{
  "id": 1,
  "name": "Paul McCartney",
  "age": 64,
  "name_and_age": "Paul McCartney (aged 64)"
}

Server-side pagination

To set the default number of results returned per page, use the results_per_page keyword argument to the APIManager.create_api() method. The default number of results per page is ten. The client can override the number of results per page by using a query parameter in its GET request; see Pagination.

To set the maximum number of results returned per page, use the max_results_per_page keyword argument. Even if results_per_page > max_results_per_page, at most max_results_per_page will be returned. The same is true if the client specifies results_per_page as a query argument; max_results_per_page provides an upper bound.

If max_results_per_page is set to anything but a positive integer, the client will be able to specify arbitrarily large page sizes. If, further, results_per_page is set to anything but a positive integer, pagination will be disabled by default, and any GET request which does not specify a page size in its query parameters will get a response with all matching results.

Attention

Disabling pagination can result in large responses!

For example, to set each page to include only two results:

apimanager.create_api(Person, results_per_page=2)

Then a request to GET /api/person will return a JSON object which looks like this:

{
  "num_results": 6,
  "total_pages": 3,
  "page": 1,
  "objects": [
    {"name": "Jeffrey", "id": 1},
    {"name": "John", "id": 2}
  ]
}

For more information on using pagination in the client, see Pagination.

Request preprocessors and postprocessors

To apply a function to the request parameters and/or body before the request is processed, use the preprocessors keyword argument. To apply a function to the response data after the request is processed (immediately before the response is sent), use the postprocessors keyword argument. Both preprocessors and postprocessors must be a dictionary which maps HTTP method names as strings (with exceptions as described below) to a list of functions. The specified functions will be applied in the order given in the list.

Since GET and PATCH (and PUT) requests can be made not only on individual instances of the model but also the entire collection of instances, you must separately specify which functions to apply in the individual case and which to apply in the collection case. For example:

# Define pre- and postprocessor functions as described below.
def pre_get_single(**kw): pass
def pre_get_many(**kw): pass
def post_patch_many(**kw): pass
def pre_delete(**kw): pass

# Create an API for the Person model.
manager.create_api(Person,
                   # Allow GET, PATCH, and POST requests.
                   methods=['GET', 'PATCH', 'DELETE'],
                   # Allow PATCH requests modifying the whole collection.
                   allow_patch_many=True,
                   # A list of preprocessors for each method.
                   preprocessors={
                       'GET_SINGLE': [pre_get_single],
                       'GET_MANY': [pre_get_many],
                       'DELETE': [pre_delete]
                       },
                   # A list of postprocessors for each method.
                   postprocessors={
                       'PATCH_MANY': [post_patch_many]
                       }
                   )

As introduced in the above example, the dictionary keys for the preprocessors and postprocessors can be one of the following strings:

  • 'GET_SINGLE' for requests to get a single instance of the model.
  • 'GET_MANY' for requests to get the entire collection of instances of the model.
  • 'PATCH_SINGLE' or 'PUT_SINGLE' for requests to patch a single instance of the model.
  • 'PATCH_MANY' or 'PUT_MANY' for requests to patch the entire collection of instances of the model.
  • 'POST' for requests to post a new instance of the model.
  • 'DELETE' for requests to delete an instance of the model.

Note

Since PUT requests are handled by the PATCH handler, any preprocessors or postprocessors specified for the PUT method will be applied on PATCH requests after the preprocessors or postprocessors specified for the PATCH method.

The preprocessors and postprocessors for each type of request accept different arguments, but none of them has a return value (more specifically, any returned value is ignored). Preprocessors and postprocessors modify their arguments in-place. The arguments to the preprocessor and postprocessor functions will be provided as keyword arguments, so you should always add **kw as the final argument when defining a preprocessor or postprocessor function. This way, you can specify only the keyword arguments you need when defining your functions.

New in version 0.13.0: Functions provided as postprocessors for GET_MANY and PATCH_MANY requests receive the search_params keyword argument, so that both preprocessors and postprocessors have access to this information.

  • GET for a single instance:

    def get_single_preprocessor(instance_id=None, **kw):
        """Accepts a single argument, `instance_id`, the primary key of the
        instance of the model to get.
    
        """
        pass
    
    def get_single_postprocessor(result=None, **kw):
        """Accepts a single argument, `result`, which is the dictionary
        representation of the requested instance of the model.
    
        """
        pass
    

    and for the collection:

    def get_many_preprocessor(search_params=None, **kw):
        """Accepts a single argument, `search_params`, which is a dictionary
        containing the search parameters for the request.
    
        """
        pass
    
    def get_many_postprocessor(result=None, search_params=None, **kw):
        """Accepts two arguments, `result`, which is the dictionary
        representation of the JSON response which will be returned to the
        client, and `search_params`, which is a dictionary containing the
        search parameters for the request (that produced the specified
        `result`).
    
        """
        pass
    
  • PATCH (or PUT) for a single instance:

    def patch_single_preprocessor(instance_id=None, data=None, **kw):
        """Accepts two arguments, `instance_id`, the primary key of the
        instance of the model to patch, and `data`, the dictionary of fields
        to change on the instance.
    
        """
        pass
    
    def patch_single_postprocessor(result=None, **kw):
        """Accepts a single argument, `result`, which is the dictionary
        representation of the requested instance of the model.
    
        """
        pass
    

    and for the collection:

    def patch_many_preprocessor(search_params=None, data=None, **kw):
        """Accepts two arguments: `search_params`, which is a dictionary
        containing the search parameters for the request, and `data`, which
        is a dictionary representing the fields to change on the matching
        instances and the values to which they will be set.
    
        """
        pass
    
    def patch_many_postprocessor(query=None, data=None, search_params=None,
                                 **kw):
        """Accepts three arguments: `query`, which is the SQLAlchemy query
        which was inferred from the search parameters in the query string,
        `data`, which is the dictionary representation of the JSON response
        which will be returned to the client, and `search_params`, which is a
        dictionary containing the search parameters for the request.
    
        """
        pass
    
  • POST:

    def post_preprocessor(data=None, **kw):
        """Accepts a single argument, `data`, which is the dictionary of
        fields to set on the new instance of the model.
    
        """
        pass
    
    def post_postprocessor(result=None, **kw):
        """Accepts a single argument, `result`, which is the dictionary
        representation of the created instance of the model.
    
        """
        pass
    
  • DELETE:

    def delete_preprocessor(instance_id=None, **kw):
        """Accepts a single argument, `instance_id`, which is the primary key
        of the instance which will be deleted.
    
        """
        pass
    
    def delete_postprocessor(was_deleted=None, **kw):
        """Accepts a single argument, `was_deleted`, which represents whether
        the instance has been deleted.
    
        """
        pass
    

Note

For more information about search parameters, see Making search queries, and for more information about request and response formats, see Format of requests and responses.

In order to halt the preprocessing or postprocessing and return an error response directly to the client, your preprocessor or postprocessor functions can raise a ProcessingException. If a function raises this exception, no preprocessing or postprocessing functions that appear later in the list specified when the API was created will be invoked. For example, an authentication function can be implemented like this:

def check_auth(instance_id=None, **kw):
    # Here, get the current user from the session.
    current_user = ...
    # Next, check if the user is authorized to modify the specified
    # instance of the model.
    if not is_authorized_to_modify(current_user, instance_id):
        raise ProcessingException(description='Not Authorized',
                                  code=401)
manager.create_api(Person, preprocessors=dict(GET_SINGLE=[check_auth]))

The ProcessingException allows you to specify an HTTP status code for the generated response and an error message which the client will receive as part of the JSON in the body of the response.

Universal preprocessors and postprocessors

New in version 0.13.0.

The previous section describes how to specify a preprocessor or postprocessor on a per-API (that is, a per-model) basis. If you want a function to be executed for all APIs created by a APIManager, you can use the preprocessors or postprocessors keyword arguments in the constructor of the APIManager class. These keyword arguments have the same format as the corresponding ones in the APIManager.create_api() method as described above. Functions specified in this way are prepended to the list of preprocessors or postprocessors specified in the APIManager.create_api() method.

This may be used, for example, if all POST requests require authentication:

from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.restless import ProcessingException
from flask.ext.login import current_user
from mymodels import User
from mymodels import session

def auth_func(*args, **kw):
    if not current_user.is_authenticated():
        raise ProcessingException(description='Not authenticated!', code=401)

app = Flask(__name__)
api_manager = APIManager(app, session=session,
                         preprocessors=dict(POST=[auth_func]))
api_manager.create_api(User)
Preprocessors for collections

When the server receives, for example, a request for GET /api/person, Flask-Restless interprets this request as a search with no filters (that is, a search for all instances of Person without exception). In other words, GET /api/person is roughly equivalent to GET /api/person?q={}. Therefore, if you want to filter the set of Person instances returned by such a request, you can create a preprocessor for a GET request to the collection endpoint that appends filters to the search_params keyword argument. For example:

def preprocessor(search_params=None, **kw):
    # This checks if the preprocessor function is being called before a
    # request that does not have search parameters.
    if search_params is None:
        return
    # Create the filter you wish to add; in this case, we include only
    # instances with ``id`` not equal to 1.
    filt = dict(name='id', op='neq', val=1)
    # Check if there are any filters there already.
    if 'filters' not in search_params:
        search_params['filters'] = []
    # *Append* your filter to the list of filters.
    search_params['filters'].append(filt)

apimanager.create_api(Person, preprocessors=dict(GET_MANY=[preprocessor]))
Custom queries

In cases where it is not possible to use preprocessors or postprocessors (Request preprocessors and postprocessors) efficiently, you can provide a custom query attribute to your model instead. The attribute can either be a callable that returns a query:

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)

    @classmethod
    def query(cls):
        return get_query_for_current_user(cls)

or a SQLAlchemy query expression:

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    query = get_some_query()
Requiring authentication for some methods

If you want certain HTTP methods to require authentication, use preprocessors:

from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.restless import NO_CHANGE
from flask.ext.restless import ProcessingException
from flask.ext.login import current_user
from mymodels import User

def auth_func(*args, **kwargs):
    if not current_user.is_authenticated():
        raise ProcessingException(description='Not authenticated!', code=401)
    return True

app = Flask(__name__)
api_manager = APIManager(app)
api_manager.create_api(User, preprocessors=dict(GET_SINGLE=[auth_func],
                                                GET_MANY=[auth_func]))

For a more complete example using Flask-Login, see the examples/server_configurations/authentication directory in the source distribution, or view it online at GitHub.

Enabling Cross-Origin Resource Sharing (CORS)

Cross-Origin Resource Sharing (CORS) is a protocol that allows JavaScript HTTP clients to make HTTP requests across Internet domain boundaries while still protecting against cross-site scripting (XSS) attacks. If you have access to the HTTP server that serves your Flask application, I recommend configuring CORS there, since such concerns are beyond the scope of Flask-Restless. However, in case you need to support CORS at the application level, you should create a function that adds the necessary HTTP headers after the request has been processed by Flask-Restless (that is, just before the HTTP response is sent from the server to the client) using the flask.Blueprint.after_request() method:

from flask import Flask
from flask.ext.restless import APIManager

def add_cors_headers(response):
    response.headers['Access-Control-Allow-Origin'] = 'example.com'
    response.headers['Access-Control-Allow-Credentials'] = 'true'
    # Set whatever other headers you like...
    return response

app = Flask(__name__)
manager = APIManager(app)
blueprint = manager.create_api(Person)
blueprint.after_request(add_cors_headers)

Making search queries

Clients can make GET requests on individual instances of a model (for example, GET /api/person/1) and on collections of all instances of a model (GET /api/person). To get all instances of a model that meet some criteria, clients can make GET requests with a query parameter specifying a search. The search functionality in Flask-Restless is relatively simple, but should suffice for many cases.

Quick examples

The following are some quick examples of creating search queries with different types of clients. Find more complete documentation in subsequent sections. In these examples, each client will search for instances of the model Person whose names contain the letter “y”.

Using the Python requests library:

import requests
import json

url = 'http://127.0.0.1:5000/api/person'
headers = {'Content-Type': 'application/json'}

filters = [dict(name='name', op='like', val='%y%')]
params = dict(q=json.dumps(dict(filters=filters)))

response = requests.get(url, params=params, headers=headers)
assert response.status_code == 200
print(response.json())

Using jQuery:

var filters = [{"name": "id", "op": "like", "val": "%y%"}];
$.ajax({
  url: 'http://127.0.0.1:5000/api/person',
  data: {"q": JSON.stringify({"filters": filters})},
  dataType: "json",
  contentType: "application/json",
  success: function(data) { console.log(data.objects); }
});

Using curl:

curl \
  -G \
  -H "Content-type: application/json" \
  -d "q={\"filters\":[{\"name\":\"name\",\"op\":\"like\",\"val\":\"%y%\"}]}" \
  http://127.0.0.1:5000/api/person

The examples/ directory has more complete versions of these examples.

Query format

The query parameter q must be a JSON string. It can have the following mappings, all of which are optional:

filters

A list of objects of one of the following forms:

{"name": <fieldname>, "op": <operatorname>, "val": <argument>}

or:

{"name": <fieldname>, "op": <operatorname>, "field": <fieldname>}

In the first form, <operatorname> is one of the strings described in the Operators section, the first <fieldname> is the name of the field of the model to which to apply the operator, <argument> is a value to be used as the second argument to the given operator. In the second form, the second <fieldname> is the field of the model that should be used as the second argument to the operator.

<fieldname> may alternately specify a field on a related model, if it is a string of the form <relationname>__<fieldname>.

If the field name is the name of a relation and the operator is "has" or "any", the "val" argument can be a dictionary with the arguments representing another filter to be applied as the argument for "has" or "any".

The returned list of matching instances will include only those instances that satisfy all of the given filters.

disjunction
A Boolean that specifies whether the list of filters should be treated as a disjunction or a conjunction. If this is true, the response will include all instances of the model that match any of the filters. If this is false the response will include all instances of the model that match all of the filters. This will be treated as false if not specified by the client (in other words, the default is conjunction).
limit
A positive integer which specifies the maximum number of objects to return.
offset
A positive integer which specifies the offset into the result set of the returned list of instances.
order_by

A list of objects of the form:

{"field": <fieldname>, "direction": <directionname>}

where <fieldname> is a string corresponding to the name of a field of the requested model and <directionname> is either "asc" for ascending order or "desc" for descending order.

single
A Boolean representing whether a single result is expected as a result of the search. If this is true and either no results or multiple results meet the criteria of the search, the server responds with an error message.

If a filter is poorly formatted (for example, op is set to '==' but val is not set), the server responds with 400 Bad Request.

Operators

The operator strings recognized by the API incude:

  • ==, eq, equals, equals_to
  • !=, neq, does_not_equal, not_equal_to
  • >, gt, <, lt
  • >=, ge, gte, geq, <=, le, lte, leq
  • in, not_in
  • is_null, is_not_null
  • like
  • has
  • any

These correspond to SQLAlchemy column operators as defined here.

Examples

Consider a Person model available at the URL /api/person, and suppose all of the following requests are GET /api/person requests with query parameter q.

Attribute greater than a value

On request:

GET /api/person?q={"filters":[{"name":"age","op":"ge","val":10}]} HTTP/1.1
Host: example.com

the response will include only those Person instances that have age attribute greater than or equal to 10:

HTTP/1.1 200 OK

{
  "num_results": 8,
  "total_pages": 3,
  "page": 2,
  "objects":
  [
    {"id": 1, "name": "Jeffrey", "age": 24},
    {"id": 2, "name": "John", "age": 13},
    {"id": 3, "name": "Mary", "age": 18}
  ]
}
Disjunction of filters

On request:

GET /api/person?q={"filters":[{"name":"age","op":"lt","val":10},{"name":"age","op":"gt","val":20}],"disjunction":true} HTTP/1.1
Host: example.com

the response will include only those Person instances that have age attribute either less than 10 or greater than 20:

HTTP/1.1 200 OK

{
  "num_results": 3,
  "total_pages": 1,
  "page": 1,
  "objects":
  [
    {"id": 4, "name": "Abraham", "age": 9},
    {"id": 5, "name": "Isaac", "age": 25},
    {"id": 6, "name": "Job", "age": 37}
  ]
}
Attribute between two values

On request:

GET /api/person?q={"filters":[{"name":"age","op":"ge","val":10},{"name":"age","op":"le","val":20}]} HTTP/1.1
Host: example.com

the response will include only those Person instances that have age attribute between 10 and 20, inclusive:

HTTP/1.1 200 OK

{
  "num_results": 6,
  "total_pages": 3,
  "page": 2,
  "objects":
  [
    {"id": 2, "name": "John", "age": 13},
    {"id": 3, "name": "Mary", "age": 18}
  ]
}
Expecting a single result

On request:

GET /api/person?q={"filters":[{"name":"id","op":"eq","val":1}],"single":true} HTTP/1.1
Host: example.com

the response will include only the sole Person instance with id equal to 1:

HTTP/1.1 200 OK

{"id": 1, "name": "Jeffrey", "age": 24}

In the case that the search would return no results or more than one result, an error response is returned instead:

GET /api/person?q={"filters":[{"name":"age","op":"ge","val":10}],"single":true} HTTP/1.1
Host: example.com
HTTP/1.1 400 Bad Request

{"message": "Multiple results found"}
GET /api/person?q={"filters":[{"name":"id","op":"eq","val":-1}],"single":true} HTTP/1.1
Host: example.com
HTTP/1.1 404 Bad Request

{"message": "No result found"}
Comparing two attributes

On request:

GET /api/person?q={"filters":[{"name":"age","op":"ge","field":"height"}]} HTTP/1.1
Host: example.com

the response will include only those Person instances that have age attribute greater than or equal to the value of the height attribute:

HTTP/1.1 200 OK

{
  "num_results": 6,
  "total_pages": 3,
  "page": 2,
  "objects":
  [
    {"id": 1, "name": "John", "age": 80, "height": 65},
    {"id": 2, "name": "Mary", "age": 73, "height": 60}
  ]
}
Comparing attribute of a relation

On request:

GET /api/person?q={"filters":[{"name":"computers__manufacturer","op":"any","val":"Apple"}],"single":true} HTTP/1.1
Host: example.com

response will include only those Person instances that are related to any Computer model that is manufactured by Apple:

HTTP/1.1 200 OK

{
  "num_results": 6,
  "total_pages": 3,
  "page": 2,
  "objects":
    {
      "id": 1,
      "name": "John",
      "computers": [
        { "id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
        { "id": 2, "manufacturer": "Apple", "model": "MacBook"}
      ]
    },
    {
      "id": 2,
      "name": "Mary",
      "computers": [
        { "id": 3, "manufacturer": "Apple", "model": "iMac"}
      ]
    }
  ]
}
Using has and any

On request:

GET /api/person?q={"filters":[{"name":"computers","op":"any","val":{"name":"id","op":"gt","val":1}}]} HTTP/1.1
Host: example.com

the response will include only those Person instances that have a related Computer instance with id field of value greater than 1:

HTTP/1.1 200 OK

{
  "num_results": 6,
  "total_pages": 3,
  "page": 2,
  "objects":
  [
    {"id": 1, "name": "John", "age": 80, "height": 65, "computers": [...]},
    {"id": 2, "name": "Mary", "age": 73, "height": 60, "computers": [...]}
  ]
}

Format of requests and responses

Requests and responses are all in JSON format, so the mimetype is application/json. Ensure that requests you make that require a body (PATCH and POST requests) have the header Content-Type: application/json; if they do not, the server will respond with a 415 Unsupported Media Type.

Suppose we have the following Flask-SQLAlchemy models (the example works with pure SQLALchemy just the same):

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)
    computers = db.relationship('Computer',
                                backref=db.backref('owner',
                                                   lazy='dynamic'))

class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    vendor = db.Column(db.Unicode)
    owner_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    purchase_time = db.Column(db.DateTime)

Also suppose we have registered an API for these models at /api/person and /api/computer, respectively.

Note

For all requests that would return a list of results, the top-level JSON object is a mapping from "objects" to the list. JSON lists are not sent as top-level objects for security reasons. For more information, see this.

GET /api/person

Gets a list of all Person objects.

Sample response:

HTTP/1.1 200 OK

{
  "num_results": 8,
  "total_pages": 3,
  "page": 2,
  "objects": [{"id": 1, "name": "Jeffrey", "age": 24}, ...]
}
GET /api/person?q=<searchjson>

Gets a list of all Person objects which meet the criteria of the specified search. For more information on the format of the value of the q parameter, see Making search queries.

Sample response:

HTTP/1.1 200 OK

{
   "num_results": 8,
   "total_pages": 3,
   "page": 2,
   "objects": [{"id": 1, "name": "Jeffrey", "age": 24}, ...]
 }
GET /api/person/(int: id)

Gets a single instance of Person with the specified ID.

Sample response:

HTTP/1.1 200 OK

{"id": 1, "name": "Jeffrey", "age": 24}
GET /api/person/(int: id)/computers

Gets a list of all Computer objects which are owned by the Person object with the specified ID.

Sample response:

HTTP/1.1 200 OK

{
  "num_results": 2,
  "total_pages": 1,
  "page": 1,
  "objects": [{"id": 1, "vendor": "Apple", "name": "MacBook", ...}, ...]
}
DELETE /api/person/(int: id)

Deletes the instance of Person with the specified ID.

Sample response:

HTTP/1.1 204 No Content
DELETE /api/person/(int: id)/computers/(int: id)

Removes the instance of Computer with the specified ID from the computers collection of the instance of Person with the specified ID. This is essentially a shortcut to using a PATCH /api/person/(int:id)/computers request with a remove parameter in the body of the request.

Sample response:

HTTP/1.1 204 No Content
POST /api/person

Creates a new person with initial attributes specified as a JSON string in the body of the request.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{"name": "Jeffrey", "age": 24}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age" 24,
  "computers": []
}

The server will respond with 400 Bad Request if the request specifies a field which does not exist on the model.

To create a new person which includes a related list of new computer instances via a one-to-many relationship, a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"manufacturer": "Dell", "model": "Inspiron"},
      {"manufacturer": "Apple", "model": "MacBook"}
    ]
}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"id": 1, "manufacturer": "Dell", "model": "Inspiron"},
      {"id": 2, "manufacturer": "Apple", "model": "MacBook"}
    ]
}

Warning

The response does not denote that new instances have been created for the Computer models.

To create a new person which includes a single related new computer instance (via a one-to-one relationship), a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computer": {"manufacturer": "Dell", "model": "Inspiron"}
}

Sample response:

HTTP/1.1 201 Created

{
  "name": "Jeffrey",
  "age": 24,
  "id": 1,
  "computer": {"id": 1, "manufacturer": "Dell", "model": "Inspiron"}
}

Warning

The response does not denote that a new Computer instance has been created.

To create a new person which includes a related list of existing computer instances via a one-to-many relationship, a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1}, {"id": 2} ]
}

Sample response:

HTTP/1.1 201 Created

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers":
    [
      {"id": 1, "manufacturer": "Dell", "model": "Inspiron"},
      {"id": 2, "manufacturer": "Apple", "model": "MacBook"}
    ]
}

To create a new person which includes a single related existing computer instance (via a one-to-one relationship), a request must take the following form.

Sample request:

POST /api/person HTTP/1.1
Host: example.com

{
  "name": "Jeffrey",
  "age": 24,
  "computer": {"id": 1}
}

Sample response:

HTTP/1.1 201 Created

{
  "name": "Jeffrey",
  "age": 24,
  "id": 1,
  "computer": {"id": 1, "manufacturer": "Dell", "model": "Inspiron"}
}
PATCH /api/person
PUT /api/person

Sets specified attributes on every instance of Person which meets the search criteria described in the q parameter.

The JSON object specified in the body of a PATCH request to this endpoint may include a mapping from q to the parameters for a search, as described in Making search queries. If no q key exists, then all instances of the model will be patched.

PUT /api/person is an alias for PATCH /api/person, because the latter is more semantically correct but the former is part of the core HTTP standard.

The response will return a JSON object which specifies the number of instances in the Person database which were modified.

Sample request:

Suppose the database contains exactly three people with the letter “y” in his or her name.

PATCH /api/person HTTP/1.1
Host: example.com

{
  "age": 1,
  "q": {"filters": [{"name": "name", "op": "like", "val": "%y%"}]}
}

Sample response:

HTTP/1.1 201 Created

{"num_modified": 3}
PATCH /api/person/(int: id)
PUT /api/person/(int: id)

Sets specified attributes on the instance of Person with the specified ID number. PUT /api/person/1 is an alias for PATCH /api/person/1, because the latter is more semantically correct but the former is part of the core HTTP standard.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{"name": "Foobar"}

Sample response:

HTTP/1.1 200 OK

{"id": 1, "name": "Foobar", "age": 24}

The server will respond with 400 Bad Request if the request specifies a field which does not exist on the model.

To add a list of existing objects to a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": [ {"id": 1} ]
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

To add a list of new objects to a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": [ {"manufacturer": "Dell", "model": "Inspiron"} ]
  }
}

Warning

The response does not denote that a new instance has been created for the Computer model.

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

Similarly, to add a new or existing instance of a related model to a one-to-one relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "add": {"id": 1}
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [ {"id": 1, "manufacturer": "Dell", "model": "Inspiron"} ]
}

To remove an existing object (without deleting that object from its own database) from a one-to-many relationship, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "remove": [ {"id": 2} ]
  }
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

To remove an existing object from a one-to-many relationship and additionally delete it from its own database, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
  {
    "remove": [ {"id": 2, "__delete__": true} ]
  }
}

Warning

The response does not denote that the instance was deleted from its own database.

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

To set the value of a one-to-many relationship to contain either existing or new instances of the related model, a request must take the following form.

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
    [
      {"id": 1},
      {"id": 3},
      {"manufacturer": "Lenovo", "model": "ThinkPad"}
    ]
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Dell", "model": "Inspiron 9300"},
    {"id": 3, "manufacturer": "Apple", "model": "MacBook"}
    {"id": 4, "manufacturer": "Lenovo", "model": "ThinkPad"}
  ]
}

To set the value of a one-to-many relationship and update fields on existing instances of the related model, a request must take the following form.

Suppose the Person instance looked like this before the sample PATCH request below:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Apple", "model": "MacBook"}
  ]
}

Sample request:

PATCH /api/person/1 HTTP/1.1
Host: example.com

{ "computers":
    [
      {"id": 1, "manufacturer": "Lenovo", "model": "ThinkPad"}
    ]
}

Sample response:

HTTP/1.1 200 OK

{
  "id": 1,
  "name": "Jeffrey",
  "age": 24,
  "computers": [
    {"id": 1, "manufacturer": "Lenovo", "model": "ThinkPad"}
  ]
}

The changes reflected in this response have been made to the Computer instance with ID 1.

Date and time fields

Flask-Restless will automatically parse and convert date and time strings into the corresponding Python objects. Flask-Restless also understands intervals (also known as durations), if you specify the interval as an integer representing the number of seconds that the interval spans.

If you want the server to set the value of a date or time field of a model as the current time (as measured at the server), use one of the special strings "CURRENT_TIMESTAMP", "CURRENT_DATE", or "LOCALTIMESTAMP". When the server receives one of these strings in a request, it will use the corresponding SQL function to set the date or time of the field in the model.

Error messages

Most errors return 400 Bad Request. A bad request, for example, will receive a response like this:

HTTP/1.1 400 Bad Request

{"message": "Unable to decode data"}

If your request triggers a SQLAlchemy DataError, IntegrityError, or ProgrammingError, the session will be rolled back.

Function evaluation

If the allow_functions keyword argument is set to True when creating an API for a model using APIManager.create_api(), then an endpoint will be made available for GET /api/eval/person which responds to requests for evaluation of functions on all instances the model.

Sample request:

GET /api/eval/person?q={"functions": [{"name": "sum", "field": "age"}, {"name": "avg", "field": "height"}]} HTTP/1.1

The format of the response is

HTTP/1.1 200 OK

{"sum__age": 100, "avg_height": 68}

If no functions are specified in the request, the response will contain the empty JSON object, {}.

Note

The functions whose names are given in the request will be evaluated using SQLAlchemy’s func object.

Example

To get the total number of rows in the query (that is, the number of instances of the requested model), use count as the name of the function to evaluate, and id for the field on which to evaluate it:

Request:

GET /api/eval/person?q={"functions": [{"name": "count", "field": "id"}]} HTTP/1.1

Response:

HTTP/1.1 200 OK

{"count__id": 5}

JSONP callbacks

Add a callback=myfunc query parameter to the request URL on any GET requests (including endpoints for function evaluation) to have the JSON data of the response wrapped in the Javascript function myfunc. This can be used to circumvent some cross domain scripting security issues. For example, a request like this:

GET /api/person/1?callback=foo HTTP/1.1

will produce a response like this:

HTTP/1.1 200 OK

foo({"meta": ..., "data": ...})

Then in your Javascript code, write the function foo like this:

function foo(response) {
  var meta, data;
  meta = response.meta;
  data = response.data;
  // Do something cool here...
}

The metadata includes the status code and the values of the HTTP headers, including the Link headers parsed in JSON format. For example, a link that looks like this:

Link: <url1>; rel="next", <url2>; rel="foo"; bar="baz"

will look like this in the JSON metadata:

[
  {"url": "url1", "rel": "next"},
  {"url": "url2", "rel": "foo", "bar": "baz"}
]

The mimetype of a JSONP response is application/javascript instead of the usual application/json, because the payload of such a response is not valid JSON.

API reference

API

This part of the documentation documents all the public classes and functions in Flask-Restless.

class flask.ext.restless.APIManager(app=None, session=None, flask_sqlalchemy_db=None, preprocessors=None, postprocessors=None)

Provides a method for creating a public ReSTful JSON API with respect to a given Flask application object.

The Flask object can be specified in the constructor, or after instantiation time by calling the init_app() method. In any case, the application object must be specified before calling the create_api() method.

app is the flask.Flask object containing the user’s Flask application.

session is the sqlalchemy.orm.session.Session object in which changes to the database will be made.

flask_sqlalchemy_db is the flask.ext.sqlalchemy.SQLAlchemy object with which app has been registered and which contains the database models for which API endpoints will be created.

If flask_sqlalchemy_db is not None, session will be ignored.

For example, to use this class with models defined in pure SQLAlchemy:

from flask import Flask
from flask.ext.restless import APIManager
from sqlalchemy import create_engine
from sqlalchemy.orm.session import sessionmaker

engine = create_engine('sqlite:////tmp/mydb.sqlite')
Session = sessionmaker(bind=engine)
mysession = Session()
app = Flask(__name__)
apimanager = APIManager(app, session=mysession)

and with models defined with Flask-SQLAlchemy:

from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLALchemy(app)
apimanager = APIManager(app, flask_sqlalchemy_db=db)
init_app(app, session=None, flask_sqlalchemy_db=None, preprocessors=None, postprocessors=None)

Stores the specified flask.Flask application object on which API endpoints will be registered and the sqlalchemy.orm.session.Session object in which all database changes will be made.

session is the sqlalchemy.orm.session.Session object in which changes to the database will be made.

flask_sqlalchemy_db is the flask.ext.sqlalchemy.SQLAlchemy object with which app has been registered and which contains the database models for which API endpoints will be created.

If flask_sqlalchemy_db is not None, session will be ignored.

This is for use in the situation in which this class must be instantiated before the Flask application has been created.

To use this method with pure SQLAlchemy, for example:

from flask import Flask
from flask.ext.restless import APIManager
from sqlalchemy import create_engine
from sqlalchemy.orm.session import sessionmaker

apimanager = APIManager()

# later...

engine = create_engine('sqlite:////tmp/mydb.sqlite')
Session = sessionmaker(bind=engine)
mysession = Session()
app = Flask(__name__)
apimanager.init_app(app, session=mysession)

and with models defined with Flask-SQLAlchemy:

from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.sqlalchemy import SQLAlchemy

apimanager = APIManager()

# later...

app = Flask(__name__)
db = SQLALchemy(app)
apimanager.init_app(app, flask_sqlalchemy_db=db)

postprocessors and preprocessors must be dictionaries as described in the section Request preprocessors and postprocessors. These preprocessors and postprocessors will be applied to all requests to and responses from APIs created using this APIManager object. The preprocessors and postprocessors given in these keyword arguments will be prepended to the list of processors given for each individual model when using the create_api_blueprint() method (more specifically, the functions listed here will be executed before any functions specified in the create_api_blueprint() method). For more information on using preprocessors and postprocessors, see Request preprocessors and postprocessors.

New in version 0.13.0: Added the preprocessors and postprocessors keyword arguments.

create_api(*args, **kw)

Creates and registers a ReSTful API blueprint on the flask.Flask application specified in the constructor of this class.

The positional and keyword arguments are passed directly to the create_api_blueprint() method, so see the documentation there.

This is a convenience method for the following code:

blueprint = apimanager.create_api_blueprint(*args, **kw)
app.register_blueprint(blueprint)

Changed in version 0.6: The blueprint creation has been moved to create_api_blueprint(); the registration remains here.

create_api_blueprint(model, methods=frozenset(['GET']), url_prefix='/api', collection_name=None, allow_patch_many=False, allow_functions=False, exclude_columns=None, include_columns=None, include_methods=None, validation_exceptions=None, results_per_page=10, max_results_per_page=100, post_form_preprocessor=None, preprocessors=None, postprocessors=None, primary_key=None)

Creates and returns a ReSTful API interface as a blueprint, but does not register it on any flask.Flask application.

The endpoints for the API for model will be available at <url_prefix>/<collection_name>. If collection_name is None, the lowercase name of the provided model class will be used instead, as accessed by model.__name__. (If any black magic was performed on model.__name__, this will be reflected in the endpoint URL.)

This function must be called at most once for each model for which you wish to create a ReSTful API. Its behavior (for now) is undefined if called more than once.

This function returns the flask.Blueprint object which handles the endpoints for the model. The returned Blueprint has already been registered with the Flask application object specified in the constructor of this class, so you do not need to register it yourself.

model is the SQLAlchemy model class for which a ReSTful interface will be created. Note this must be a class, not an instance of a class.

methods specify the HTTP methods which will be made available on the ReSTful API for the specified model, subject to the following caveats:

  • If GET is in this list, the API will allow getting a single instance of the model, getting all instances of the model, and searching the model using search parameters.
  • If PATCH is in this list, the API will allow updating a single instance of the model, updating all instances of the model, and updating a subset of all instances of the model specified using search parameters.
  • If DELETE is in this list, the API will allow deletion of a single instance of the model per request.
  • If POST is in this list, the API will allow posting a new instance of the model per request.

The default set of methods provides a read-only interface (that is, only GET requests are allowed).

collection_name is the name of the collection specified by the given model class to be used in the URL for the ReSTful API created. If this is not specified, the lowercase name of the model will be used.

url_prefix the URL prefix at which this API will be accessible.

If allow_patch_many is True, then requests to /api/ will attempt to patch the attributes on each of the instances of the model which match the specified search query. This is False by default. For information on the search query parameter q, see Making search queries.

validation_exceptions is the tuple of possible exceptions raised by validation of your database models. If this is specified, validation errors will be captured and forwarded to the client in JSON format. For more information on how to use validation, see Capturing validation errors.

If allow_functions is True, then requests to /api/eval/ will return the result of evaluating SQL functions specified in the body of the request. For information on the request format, see Function evaluation. This if False by default. Warning: you must not create an API for a model whose name is 'eval' if you set this argument to True.

If either include_columns or exclude_columns is not None, exactly one of them must be specified. If both are not None, then this function will raise a IllegalArgumentError. exclude_columns must be an iterable of strings specifying the columns of model which will not be present in the JSON representation of the model provided in response to GET requests. Similarly, include_columns specifies the only columns which will be present in the returned dictionary. In other words, exclude_columns is a blacklist and include_columns is a whitelist; you can only use one of them per API endpoint. If either include_columns or exclude_columns contains a string which does not name a column in model, it will be ignored.

If include_columns is an iterable of length zero (like the empty tuple or the empty list), then the returned dictionary will be empty. If include_columns is None, then the returned dictionary will include all columns not excluded by exclude_columns.

If include_methods is an iterable of strings, the methods with names corresponding to those in this list will be called and their output included in the response.

See Specifying which columns are provided in responses for information on specifying included or excluded columns on fields of related models.

results_per_page is a positive integer which represents the default number of results which are returned per page. Requests made by clients may override this default by specifying results_per_page as a query argument. max_results_per_page is a positive integer which represents the maximum number of results which are returned per page. This is a “hard” upper bound in the sense that even if a client specifies that greater than max_results_per_page should be returned, only max_results_per_page results will be returned. For more information, see Server-side pagination.

Deprecated since version 0.9.2: The post_form_preprocessor keyword argument is deprecated in version 0.9.2. It will be removed in version 1.0. Replace code that looks like this::

manager.create_api(Person, post_form_preprocessor=foo)

with code that looks like this:

manager.create_api(Person, preprocessors=dict(POST=[foo]))

See Request preprocessors and postprocessors for more information and examples.

post_form_preprocessor is a callback function which takes POST input parameters loaded from JSON and enhances them with other key/value pairs. The example use of this is when your model requires to store user identity and for security reasons the identity is not read from the post parameters (where malicious user can tamper with them) but from the session.

preprocessors is a dictionary mapping strings to lists of functions. Each key is the name of an HTTP method (for example, 'GET' or 'POST'). Each value is a list of functions, each of which will be called before any other code is executed when this API receives the corresponding HTTP request. The functions will be called in the order given here. The postprocessors keyword argument is essentially the same, except the given functions are called after all other code. For more information on preprocessors and postprocessors, see Request preprocessors and postprocessors.

primary_key is a string specifying the name of the column of model to use as the primary key for the purposes of creating URLs. If the model has exactly one primary key, there is no need to provide a value for this. If model has two or more primary keys, you must specify which one to use.

New in version 0.13.0: Added the primary_key keyword argument.

New in version 0.10.2: Added the include_methods keyword argument.

Changed in version 0.10.0: Removed authentication_required_for and authentication_function keyword arguments.

Use the preprocesors and postprocessors keyword arguments instead. For more information, see Requiring authentication for some methods.

New in version 0.9.2: Added the preprocessors and postprocessors keyword arguments.

New in version 0.9.0: Added the max_results_per_page keyword argument.

New in version 0.7: Added the exclude_columns keyword argument.

New in version 0.6: This functionality was formerly in create_api(), but the blueprint creation and registration have now been separated.

New in version 0.6: Added the results_per_page keyword argument.

New in version 0.5: Added the include_columns and validation_exceptions keyword argument.

New in version 0.4: Added the allow_functions, allow_patch_many, authentication_required_for, authentication_function, and collection_name keyword arguments.

New in version 0.4: Force the model name in the URL to lowercase.

class flask.ext.restless.ProcessingException(description='', code=400, *args, **kwargs)

Raised when a preprocessor or postprocessor encounters a problem.

This exception should be raised by functions supplied in the preprocessors and postprocessors keyword arguments to APIManager.create_api. When this exception is raised, all preprocessing or postprocessing halts, so any processors appearing later in the list will not be invoked.

status_code is the HTTP status code of the response supplied to the client in the case that this exception is raised. message is an error message describing the cause of this exception. This message will appear in the JSON object in the body of the response to the client.

Additional information

Similar projects

If Flask-Restless doesn’t work for you, here are some similar Python packages that intend to simplify the creation of ReSTful APIs (in various combinations of Web frameworks and database backends):

Changelog

Here you can see the full list of changes between each Flask-Restless release. Numbers following a pound sign (#) refer to GitHub issues.

Note

As of version 0.13.0, Flask-Restless supports Python 2.6, 2.7, and 3. Before that, it supported Python 2.5, 2.6, and 2.7.

Note

As of version 0.6, Flask-Restless supports both pure SQLAlchemy and Flask-SQLAlchemy models. Before that, it supported only Elixir models.

Version 0.14.2

Released on September 2, 2014.

  • #351, #355: fixes bug in getting related models from a model with hybrid properties.

Version 0.14.1

Released on August 26, 2014.

  • #210: lists some related projects in the documentation.
  • #347: adds automated build testing for PyPy 3.
  • #354: renames is_deleted to was_deleted when providing keyword arguments to postprocessor for DELETE method in order to match documentation.

Version 0.14.0

Released on August 12, 2014.

  • Fixes bug where primary key specified by user was not being checked in some POST requests and some search queries.
  • #223: documents CORS example.
  • #280: don’t expose raw SQL in responses on database errors.
  • #299: show error message if search query tests for NULL using comparison operators.
  • #315: check for query object being None.
  • #324: DELETE should only return 204 No Content if something is actuall deleted.
  • #325: support null inside has search operators.
  • #328: enable automatic testing for Python 3.4.
  • #333: enforce limit in helpers.count().
  • #338: catch validation exceptions when attempting to update relations.
  • #339: use user-specified primary key on PATCH requests.
  • #344: correctly encodes Unicode fields in responses.

Version 0.13.1

Released on April 21, 2014.

  • #304: fixes mimerender bug due to how Python 3.4 handles decorators.

Version 0.13.0

Released on April 6, 2014.

  • Allows universal preprocessors or postprocessors; see Universal preprocessors and postprocessors.
  • Allows specifying which primary key to use when creating endpoint URLs.
  • Requires SQLAlchemy version 0.8 or greater.
  • #17: use Flask’s flask.Request.json to parse incoming JSON requests.
  • #29: replace custom jsonify_status_code function with built-in support for return jsonify(), status_code style return statements (new in Flask 0.9).
  • #51: Use mimerender to render dictionaries to JSON format.
  • #247: adds support for making POST requests to dictionary-like association proxies.
  • #249: returns 404 Not Found if a search reveals no matching results.
  • #254: returns 404 Not Found if no related field exists for a request with a related field in the URL.
  • #256: makes search parameters available to postprocessors for GET and PATCH requests that access multiple resources.
  • #263: Adds Python 3.3 support; drops Python 2.5 support.
  • #267: Adds compatibility for legacy Microsoft Internet Explorer versions 8 and 9.
  • #270: allows the query attribute on models to be a callable.
  • #282: order responses by primary key if no order is specified.
  • #284: catch DataError and ProgrammingError exceptions when bad data are sent to the server.
  • #286: speed up paginated responses by using optimized count() function.
  • #293: allows sqlalchemy.Time fields in JSON responses.

Version 0.12.1

Released on December 1, 2013.

  • #222: on POST and PATCH requests, recurse into nested relations to get or create instances of related models.
  • #246: adds pysqlite to test requirements.
  • #260: return a single object when making a GET request to a relation sub-URL.
  • #264: all methods now execute postprocessors after setting headers.
  • #265: convert strings to dates in related models when making POST requests.

Version 0.12.0

Released on August 8, 2013.

  • #188: provides metadata as well as normal data in JSONP responses.
  • #193: allows DELETE requests to related instances.
  • #215: removes Python 2.5 tests from Travis configuration.
  • #216: don’t resolve Query objects until pagination function.
  • #217: adds missing indices in format string.
  • #220: fix bug when checking attributes on a hybrid property.
  • #227: allows client to request that the server use the current date and/or time when setting the value of a field.
  • #228 (as well as #212, #218, #231): fixes issue due to a module removed from Flask version 0.10.

Version 0.11.0

Released on May 18, 2013.

  • Requests that require a body but don’t have Content-Type: application/json will cause a 415 Unsupported Media Type response.
  • Responses now have Content-Type: application/json.
  • #180: allow more expressive has and any searches.
  • #195: convert UUID objects to strings when converting an instance of a model to a dictionary.
  • #202: allow setting hybrid properties with expressions and setters.
  • #203: adds the include_methods keyword argument to APIManager.create_api(), which allows JSON responses to include the result of calling arbitrary methods of instances of models.
  • #204, 205: allow parameters in Content-Type header.

Version 0.10.1

Released on May 8, 2013.

  • #115: change assertEqual() methods to assert statements in tests.
  • #184, #186: Switch to nose for testing.
  • #197: documents technique for adding filters in processors when there are none initially.

Version 0.10.0

Released on April 30, 2013.

  • #2: adds basic GET access to one level of relationship depth for models.
  • #113: interpret empty strings for date fields as None objects.
  • #115: use Python’s built-in assert statements for testing
  • #128: allow disjunctions when filtering search queries.
  • #130: documentation and examples now more clearly show search examples.
  • #135: added support for hybrid properties.
  • #139: remove custom code for authentication in favor of user-defined pre- and postprocessors (this supercedes the fix from #154).
  • #141: relax requirement for version of python-dateutil to be not equal to 2.0 if using Python version 2.6 or 2.7.
  • #146: preprocessors now really execute before other code.
  • #148: adds support for SQLAlchemy association proxies.
  • #154 (this fix is irrelevant due to #139): authentication function now may raise an exception instead of just returning a Boolean.
  • #157: POST requests now receive a response containing all fields of the created instance.
  • #162: allow pre- and postprocessors to indicate that no change has occurred.
  • #164, #172, and #173: PATCH requests update fields on related instances.
  • #165: fixed bug in automatic exposing of URLs for related instances.
  • #170: respond with correct HTTP status codes when a query for a single instance results in none or multiple instances.
  • #174: allow dynamically loaded relationships for automatically exposed URLs of related instances.
  • #176: get model attribute instead of column name when getting name of primary key.
  • #182: allow POST requests that set hybrid properties.
  • #152: adds some basic server-side logging for exceptions raised by views.

Version 0.9.3

Released on February 4, 2013.

  • Fixes incompatibility with Python 2.5 try/except syntax.
  • #116: handle requests which raise IntegrityError.

Version 0.9.2

Released on February 4, 2013.

  • #82, #134, #136: added request pre- and postprocessors.
  • #120: adds support for JSON-P callbacks in GET requests.

Version 0.9.1

Released on January 17, 2013.

  • #126: fix documentation build failure due to bug in a dependency.
  • #127: added “ilike” query operator.

Version 0.9.0

Released on January 16, 2013.

  • Removed ability to provide a Session class when initializing APIManager; provide an instance of the class instead.
  • Changes some dynamically loaded relationships used for testing and in examples to be many-to-one instead of the incorrect one-to-many. Versions of SQLAlchemy after 0.8.0b2 raise an exception when the latter is used.
  • #105: added ability to set a list of related model instances on a model.
  • #107: server responds with an error code when a PATCH or POST request specifies a field which does not exist on the model.
  • #108: dynamically loaded relationships should now be rendered correctly by the views._to_dict() function regardless of whether they are a list or a single object.
  • #109: use sphinxcontrib-issuetracker to render links to GitHub issues in documentation.
  • #110: enable results_per_page query parameter for clients, and added max_results_per_page keyword argument to APIManager.create_api().
  • #114: fix bug where string representations of integers were converted to integers.
  • #117: allow adding related instances on PATCH requests for one-to-one relationships.
  • #123: PATCH requests to instances which do not exist result in a 404 Not Found response.

Version 0.8.0

Released on November 19, 2012.

  • #94: views._to_dict() should return a single object instead of a list when resolving dynamically loaded many-to-one relationships.
  • #104: added num_results key to paginated JSON responses.

Version 0.7.0

Released on October 9, 2012.

  • Added working include and exclude functionality to the views._to_dict() function.
  • Added exclude_columns keyword argument to APIManager.create_api().
  • #79: attempted to access attribute of None in constructor of APIManager.
  • #83: allow POST requests with one-to-one related instances.
  • #86: allow specifying include and exclude for related models.
  • #91: correctly handle POST requests to nullable DateTime columns.
  • #93: Added a total_pages mapping to the JSON response.
  • #98: GET requests to the function evaluation endpoint should not have a data payload.
  • #101: exclude in views._to_dict() function now correctly excludes requested fields from the returned dictionary.

Version 0.6

Released on June 20, 2012.

  • Added support for accessing model instances via arbitrary primary keys, instead of requiring an integer column named id.
  • Added example which uses curl as a client.
  • Added support for pagination of responses.
  • Fixed issue due to symbolic link from README to README.md when running pip bundle foobar Flask-Restless.
  • Separated API blueprint creation from registration, using APIManager.create_api() and APIManager.create_api_blueprint().
  • Added support for pure SQLAlchemy in addition to Flask-SQLAlchemy.
  • #74: Added post_form_preprocessor keyword argument to APIManager.create_api().
  • #77: validation errors are now correctly handled on PATCH requests.

Version 0.5

Released on April 10, 2012.

  • Dual-licensed under GNU AGPLv3+ and 3-clause BSD license.
  • Added capturing of exceptions raised during field validation.
  • Added examples/separate_endpoints.py, showing how to create separate API endpoints for a single model.
  • Added include_columns keyword argument to create_api() method to allow users to specify which columns of the model are exposed in the API.
  • Replaced Elixir with Flask-SQLAlchemy. Flask-Restless now only supports Flask-SQLAlchemy.

Version 0.4

Released on March 29, 2012.

  • Added Python 2.5 and Python 2.6 support.
  • Allow users to specify which HTTP methods for a particular API will require authentication and how that authentication will take place.
  • Created base classes for test cases.
  • Moved the evaluate_functions function out of the flask_restless.search module and corrected documentation about how function evaluation works.
  • Added allow_functions keyword argument to create_api().
  • Fixed bug where we weren’t allowing PUT requests in create_api().
  • Added collection_name keyword argument to create_api() to allow user provided names in URLs.
  • Added allow_patch_many keyword argument to create_api() to allow enabling or disabling the PATCH many functionality.
  • Disable the PATCH many functionality by default.

Version 0.3

Released on March 4, 2012.

  • Initial release in Flask extension format.