Flask-Restful
基本
flaskのみだと
if request.method == "GET": ...
な感じで,HTTP Methodと振る舞いを対応付けるが,これをやってくれるのが,Flask-RESTful. Resourceクラスを拡張したクラスとURL Routingを書いてそれらを紐付ける.
Basic Sample Code
from flask import Flask, request from flask_restful import Resource, Api app = Flask(__name__) api = Api(app) # Hello World class HelloWorld(Resource): def get(self): return {'hello': 'world by get'} def post(self): return {'hello': 'world by post'} def put(self): return {'hello': 'world by put'} def delete(self): return {'hello': 'world by delete'} api.add_resource(HelloWorld, '/hello') # call like """ curl -X GET "http://localhost:5000/hello" curl -X POST "http://localhost:5000/hello" curl -X put "http://localhost:5000/hello" curl -X DELETE "http://localhost:5000/hello" """ # Todo Sample 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) # call like """ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT curl http://localhost:5000/todo1 curl http://localhost:5000/todo2 -d "data=Change my brakepads" -X PUT curl http://localhost:5000/todo2 """
メジャー機能
- クラス内で,HTTP Methodで振り分ける
- Endpoints
- パラメータのバリデーション機能
- リスポンスクラスを定義してそれでレスを返す
- Blueprintとの連携
Endpoints
Resourceを拡張したクラスとURL routingを1対1で対応付けるが,1対 nでも大丈夫.
api.add_resource(HelloWorld, '/', '/hello')
Endpointを設定
api.add_resource(Todo, '/todo/<int:todo_id>', endpoint='todo_ep')
Argument Parsing (Request Parameter Validation)
どこからパースするか?,答えは,
By default, the RequestParser tries to parse values from flask.Request.values, and flask.Request.json.
なので,form dataと"application/json"のcontent-typeでデータが来た時にそのデータをパースすると思っていい.
- どこ(query, form, header, cookie, file, etc)から読みだすかは変更可能
- Common Argsに関するパーサーは使い回したいから継承も可能
- 値がlistの場合はaction="append"とすること
Argument Parsing Sample Coce
from flask import Flask from flask_restful import Resource, Api, reqparse app = Flask(__name__) api = Api(app) # Hello World class HelloWorld(Resource): parser = reqparse.RequestParser() parser.add_argument('rate', type=int, help='Rate cannot be converted') parser.add_argument('name', required=True) parser.add_argument('name_relocated', dest="name_dest") parser.add_argument('todos', type=str, action="append") def post(self): args = self.parser.parse_args() print args return args api.add_resource(HelloWorld, '/hello') if __name__ == '__main__': app.run(debug=True)
Output Fields
普通にデータ dict で返せばいいが,RESをクラスで定義して,返したいときもある.そういう時に, "fields" module and "the marshal_with()" decoratorの機能がある.Django ORM and WTFormに似ているそう.
resource_fields (=dict object)とレスポンスクラスを作って,コントローラークラス(Resourceを継承したクラス)のなかで,
resource_fields = { ... } @marshal_with(resource_fields) def get(self, ...): ...
な感じで使用する
- 独自フィールドも作成可能
- ListやNestedフィールドも作成可能
Ouptut Fields Sample Code
from flask_restful import fields, marshal_with from flask import Flask from flask_restful import Resource, Api from flask_restful import fields, marshal_with # Restful Basics app = Flask(__name__) api = Api(app) # fields module and marshal_with decorator user_fields = { 'id': fields.Integer, 'name': fields.String, } resource_fields = { 'task_id': fields.Integer, 'task': fields.String(default="Default task"), 'tasks': fields.List(fields.String(default="Default task")), 'users': fields.List(fields.Nested(user_fields)), #'uri': fields.Url('todo_ep') } class TodoDao(object): def __init__(self, task_id, task, tasks, users): self.task_id = task_id self.task = task self.tasks = tasks self.users = users # This field will not be sent in the response (meaning can have other state) self.status = 'active' class Todo(Resource): @marshal_with(resource_fields) def get(self, task_id): task_id = task_id task = 'Remember the milk' tasks = ["Remember the milk 000", "Remember the milk 001", "Remember the milk 002'"] users = [ {"id": 0, "name": "kzky000"}, {"id": 1, "name": "kzky001"}, {"id": 2, "name": "kzky002"} ] return TodoDao(task_id=task_id, task=task, tasks=tasks, users=users) api.add_resource(Todo, '/todos/<int:task_id>') if __name__ == '__main__': app.run(debug=True) # call like """ curl -X GET "http://localhost:5000/todos/10" """
Use with Blueprint
当然Blueprintとの連携も可能
With Blurpint Sample Code
from flask import Flask, Blueprint from flask_restful import Api, Resource app = Flask(__name__) api_bp = Blueprint('api', "flask-restful with blueprint", url_prefix="/api",) api = Api(api_bp) class TodoItem(Resource): def get(self, id): return {'task': 'Say "Hello, World!" for {}'.format(id)} api.add_resource(TodoItem, '/todos/<int:id>') app.register_blueprint(api_bp) if __name__ == '__main__': app.run(debug=True) # call like """ curl -X GET "http://localhost:5000/api/todos/10" """
Sample Codes
すべてここにあるflask_restful_のprefixのやつ
設計方針
1つのファイルに
- reqeust parser
- fields
- response class
- resoruce class
を書けばいいと思う.
- reqeust parser
- fields
は,resoruce classの中にclass memberとして書いたほうがいいかも
URLs
- http://flask-restful-cn.readthedocs.org/en/0.3.4/quickstart.html
- http://flask-restful-cn.readthedocs.org/en/0.3.4/reqparse.html
- http://flask-restful-cn.readthedocs.org/en/0.3.4/fields.html
- http://flask-restful-cn.readthedocs.org/en/0.3.4/api.html
- http://flask-restful-cn.readthedocs.org/en/0.3.4/intermediate-usage.html
- http://flask-restful-cn.readthedocs.org/en/0.3.4/api.html#module-fields