from flask import Flask from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) class HelloWorld(Resource): def get(self): return {'hello': 'world'} api.add_resource(HelloWorld, '/') if __name__ == '__main__': app.run(debug=True)
To define verbs for a resource you use a class which inherits from Resource, then you override its methods. In the next example we create a class with GET and PUT methods. For PUT method we accept data:
from flask import Flask, request from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) todos = {} class TodoSimple(Resource): def get(self, todo_id): return {todo_id: todos[todo_id]} def put(self, todo_id): todos[todo_id] = request.form['data'] return {todo_id: todos[todo_id]} api.add_resource(TodoSimple, '/<string:todo_id>') if __name__ == '__main__': app.run(debug=True)
Then…
>>> from requests import put, get
>>> put('http://localhost:5000/todo1', data={'data': 'Remember the milk'}).json()
{u'todo1': u'Remember the milk'}
>>> get('http://localhost:5000/todo1').json()
{u'todo1': u'Remember the milk'}
We also could return custom result codes and headers:
# Default to 200 OK return {'task': 'Hello world'} # Set the response code to 201 return {'task': 'Hello world'}, 201 # Set the response code to 201 and return custom headers return {'task': 'Hello world'}, 201, {'Etag': 'some-opaque-string'}
You can add more url's for a resource:
api.add_resource(HelloWorld, '/', '/hello')
In the request object you could access to the endpoint property. This is set as the lowercase of the class name, however you can change it:
api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')
Flask RESTful could suggest a proper endpoint if the one provided by the user does not match. You can change it setting ERROR_404_HELP to False in config.
Flask RESTful framework has the reqparse tool for sended data in the request form. Its method parse_args() returns a dictionary.
from flask_restful import reqparse parser = reqparse.RequestParser() parser.add_argument('rate', type=int, help='Rate to charge for this resource') args = parser.parse_args()
Using it you ensure the sended data is right, if not a 404 error will be return. Specially if you call it with strict=True, then it ensures that an error is thrown if the request includes arguments your parser does not define.
args = parser.parse_args(strict=True)
Also you can use inputs module. It allows you to obtain formated data (for example using regex, dates, basic data like ints or strings, urls…):
parser = reqparse.RequestParser() parser.add_argument('example', type=inputs.regex('^[0-9]+$'))
To indicate a required argument:
parser.add_argument('name', required=True, help="Name cannot be blank!")
To indicate a list as argument:
parser.add_argument('name', action='append') # curl http://api.example.com -d "name=bob" -d "name=sue" -d "name=joe" args = parser.parse_args() args['name'] # ['bob', 'sue', 'joe']
Or you want to change the destination:
parser.add_argument('name', dest='public_name') args = parser.parse_args() args['public_name']
You can set the locations of the parameter (querystring, POST data…)
# Look only in the POST body parser.add_argument('name', type=int, location='form') # Look only in the querystring parser.add_argument('PageSize', type=int, location='args') # From the request headers parser.add_argument('User-Agent', location='headers') # From http cookies parser.add_argument('session_id', location='cookies') # From file uploads parser.add_argument('picture', type=werkzeug.datastructures.FileStorage, location='files') # Multiple locations parser.add_argument('text', location=['headers', 'values'])
You can specify which data you want to return using fields module and @marshal_with decorator. Fields module allows you to use ORM's and Python objects to return them and format them.
resource_fields = { 'name': fields.String, 'address': fields.String, 'date_updated': fields.DateTime(dt_format='rfc822'), }
You can specify the data to be sent using @marshal_with. It serializes a python object. In the next example it takes the url of the todo_ep end point (you can take a look to other fields: api.html).
from collections import OrderedDict from flask_restful import fields, marshal_with resource_fields = { 'task': fields.String, 'uri': fields.Url('todo_ep') } class TodoDao(object): def __init__(self, todo_id, task): self.todo_id = todo_id self.task = task # This field will not be sent in the response self.status = 'active' class Todo(Resource): @marshal_with(resource_fields) def get(self, **kwargs): return TodoDao(todo_id='my_todo', task='Remember the milk')
@marshal_with is a decorator for the function marshal:
def get(self, **kwargs): return marshal(db_get_todo(), resource_fields), 200
With fields you can rename or format attributes. Even you can give a default value:
fields = { 'name': fields.String(attribute='private_name', default='No name'), 'address': fields.String, } fields = { 'name': fields.String(attribute=lambda x: x._private_name), 'address': fields.String, }
To give a custom format:
class UrgentItem(fields.Raw): def format(self, value): return "Urgent" if value & 0x01 else "Normal" class UnreadItem(fields.Raw): def format(self, value): return "Unread" if value & 0x02 else "Read" fields = { 'name': fields.String, 'priority': UrgentItem(attribute='flags'), 'status': UnreadItem(attribute='flags'), }
To return an absolute url from a resoruce:
fields = { 'uri': fields.Url('todo_resource', absolute=True) 'https_uri': fields.Url('todo_resource', absolute=True, scheme='https') }
You can return a field list:
>>> from flask_restful import fields, marshal >>> import json >>> resource_fields = {'name': fields.String, 'first_names': fields.List(fields.String)} >>> data = {'name': 'Bougnazal', 'first_names' : ['Emile', 'Raoul']} >>> json.dumps(marshal(data, resource_fields)) >>> '{"first_names": ["Emile", "Raoul"], "name": "Bougnazal"}'
Or a nested field:
user_fields = { 'id': fields.Integer, 'name': fields.String, } user_list_fields = { fields.List(fields.Nested(user_fields)), }
>>> from flask_restful import fields, marshal >>> import json >>> >>> resource_fields = {'name': fields.String} >>> resource_fields['address'] = {} >>> resource_fields['address']['line 1'] = fields.String(attribute='addr1') >>> resource_fields['address']['line 2'] = fields.String(attribute='addr2') >>> resource_fields['address']['city'] = fields.String >>> resource_fields['address']['state'] = fields.String >>> resource_fields['address']['zip'] = fields.String >>> data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> json.dumps(marshal(data, resource_fields)) '{"name": "bob", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "zip": "10468", "city": "New York"}}'
>>> from flask_restful import fields, marshal >>> import json >>> >>> address_fields = {} >>> address_fields['line 1'] = fields.String(attribute='addr1') >>> address_fields['line 2'] = fields.String(attribute='addr2') >>> address_fields['city'] = fields.String(attribute='city') >>> address_fields['state'] = fields.String(attribute='state') >>> address_fields['zip'] = fields.String(attribute='zip') >>> >>> resource_fields = {} >>> resource_fields['name'] = fields.String >>> resource_fields['billing_address'] = fields.Nested(address_fields) >>> resource_fields['shipping_address'] = fields.Nested(address_fields) >>> address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'} >>> data = { 'name': 'bob', 'billing_address': address1, 'shipping_address': address2} >>> >>> json.dumps(marshal_with(data, resource_fields)) '{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'
You can have in an app.py file the defined end points:
from flask import Flask from flask_restful import Api from myapi.resources.foo import Foo from myapi.resources.bar import Bar from myapi.resources.baz import Baz app = Flask(__name__) api = Api(app) api.add_resource(Foo, '/Foo', '/Foo/<str:id>') api.add_resource(Bar, '/Bar', '/Bar/<str:id>') api.add_resource(Baz, '/Baz', '/Baz/<str:id>')
That app.py would configure the API. You could also add there actions for before_request() and after_request() functions.
from flask import Flask, Blueprint from flask_restful import Api, Resource, url_for app = Flask(__name__) api_bp = Blueprint('api', __name__) api = Api(api_bp) class TodoItem(Resource): def get(self, id): return {'task': 'Say "Hello, World!"'} api.add_resource(TodoItem, '/todos/<int:id>') app.register_blueprint(api_bp)
abort(404, message=“Todo {} doesn't exist”.format(todo_id))