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:
- They must have a primary key column of type
sqlalchemy.Integer
or typesqlalchemy.Unicode
. - They must have an
__init__
method which accepts keyword arguments for all columns (the constructor inflask.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
.
Initializing the Flask application after creating the API manager¶
Instead of providing the Flask application at instantiation time, you can
initialize the Flask application after instantiating the APIManager
object by using the APIManager.init_app()
method. If you do this, you
will need to provide the Flask application object using the app
keyword
argument to the APIManager.create_api()
method:
from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
manager = APIManager(flask_sqlalchemy_db=db)
# later...
manager.init_app(app)
manager.create_api(Person, app=app)
You can also use this approach to initialize multiple Flask applications with a
single instance of APIManager
. For example:
from flask import Flask
from flask.ext.restless import APIManager
from flask.ext.sqlalchemy import SQLAlchemy
# Create two Flask applications, both backed by the same database.
app1 = Flask(__name__)
app2 = Flask(__name__ + '2')
app1.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
app2.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app1)
# Create the Flask-SQLAlchemy models.
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)
# Create the database tables.
db.create_all()
# Create the APIManager and initialize it with the different Flask objects.
manager = APIManager(flask_sqlalchemy_db=db)
manager.init_app(app1)
manager.init_app(app2)
# When creating each API, you need to specify which Flask application
# should be handling these requests.
manager.create_api(Person, app=app1)
manager.create_api(Computer, app=app2)
Finally, you can also create an API before initializing the Flask application. For example:
manager = APIManager()
manager.create_api(Person)
manager.init_app(app, session=session)
Changed in version 0.16.0: The APIManager.init_app()
method behaved incorrectly before version
0.16.0. From that version on, you must provide the Flask application when
you call APIManager.create_api()
after having performed the delayed
initialization described in this section.