あいつの日誌β

働きながら旅しています。

Flask 0.10.1 rest api tutorial

enviroments

% uname -v
Darwin Kernel Version 13.4.0: Sun Aug 17 19:50:11 PDT 2014; root:xnu-2422.115.4~1/RELEASE_X86_64

% python --version
Python 3.4.2

% pip show Flask
---
Name: Flask
Version: 0.10.1
Location: /Users/okamuuu/local/python-3.4.2/lib/python3.4/site-packages
Requires: Werkzeug, Jinja2, itsdangerous

prepare

create myserver.py

import flask

app = flask.Flask(__name__)

@app.route('/')
def index():
    return "Hello, World!"

if __name__ == '__main__':
    app.run(debug=True)

create myserver_test.py

import myserver
import unittest

class MyServerTestCase(unittest.TestCase):

    def setUp(self):
        myserver.app.config['TESTING'] = True
        self.app = myserver.app.test_client()

    def test_hello(self):
        rv = self.app.get('/')
        assert b'Hello, World' in rv.data

if __name__ == '__main__':
    unittest.main()

do it.

% python myserver_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.012s

OK

edit test case and implement it.

edit myserver_test.py like this.

% git diff
diff --git a/myserver_test.py b/myserver_test.py
index 82365d9..4f8da0d 100644
--- a/myserver_test.py
+++ b/myserver_test.py
@@ -1,5 +1,6 @@
 import myserver
 import unittest
+import json
 
 class MyServerTestCase(unittest.TestCase):
 
@@ -11,5 +12,14 @@ class MyServerTestCase(unittest.TestCase):
         rv = self.app.get('/')
         assert b'Hello, World' in rv.data
 
+    def test_get_tasks(self):
+        response = self.app.get('/api/v1.0/tasks')
+        assert response.status_code == 200
+        assert response.headers['Content-Type'] == 'application/json'
+        content_body_dict = json.loads(response.data.decode())
+        tasks = content_body_dict['tasks']
+        assert tasks[0].get('id') is not None
+        assert tasks[0].get('title') is not None
+
 if __name__ == '__main__':
     unittest.main()

edit myserver.py

% git diff myserver.py
diff --git a/myserver.py b/myserver.py
index 45504a5..311c7f3 100644
--- a/myserver.py
+++ b/myserver.py
@@ -6,5 +6,17 @@ app = flask.Flask(__name__)
 def index():
     return "Hello, World!"
 
+@app.route('/api/v1.0/tasks', methods=['GET'])
+def get_tasks():
+
+    response = flask.jsonify({'tasks': [
+        {
+            'id': 1,
+            'title': 'my first todo'
+        }
+    ]})
+    response.status_code = 200
+    return response
+
 if __name__ == '__main__':
     app.run(debug=True)

test it.

% python myserver_test.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.014s

OK

add test case using storage and implement it

create schema.sql

drop table if exists tasks;
create table tasks (
  id integer primary key autoincrement,
  title text not null
);

create mydb_test.py

import mydb
import unittest

class MyDbTestCase(unittest.TestCase):

    def setUp(self):
        self.db = mydb.MyDB('/tmp/test_mydb.db')
        self.db.init()

    def test_tasks(self):
        tasks = self.db.get_tasks()
        assert len(tasks) is 0
        
        self.db.add_task('title1')
        tasks = self.db.get_tasks()
        assert len(tasks) is 1

if __name__ == '__main__':
    unittest.main()

create mydb.py

import sqlite3
from contextlib import closing

class MyDB:

    def __init__(self, database):
        self.database = database

    def connect(self):
        return sqlite3.connect(self.database)

    def init(self):
        with closing(self.connect()) as db:
            with open('schema.sql', mode='r') as f:
                db.cursor().executescript(f.read())
            db.commit()

    def get_tasks(self):
      db = self.connect()
      cur = db.execute('select * from tasks order by id desc')
      tasks = [dict(id=row[0], title=row[1]) for row in cur.fetchall()]
      db.close();
      return tasks

    def add_task(self, title):
      db = self.connect()
      db.execute('insert into tasks (title) values (?)', [title])
      db.commit()
      db.close()

test it.

% python mydb_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.005s

OK

edit myserver.py

diff --git a/myserver.py b/myserver.py
index 311c7f3..6e9c418 100644
--- a/myserver.py
+++ b/myserver.py
@@ -1,7 +1,19 @@
 import flask
+import mydb
+import os
 
 app = flask.Flask(__name__)

+if os.getenv('TEST') == 'True':
+    app.config['DATABASE'] = '/tmp/test_mydb.db'
+else:
+    app.config['DATABASE'] = '/tmp/mydb.db'
+
+db = mydb.MyDB(app.config['DATABASE'])
+db.init()
+
 @app.route('/')
 def index():
     return "Hello, World!"
@@ -9,13 +21,24 @@ def index():
 @app.route('/api/v1.0/tasks', methods=['GET'])
 def get_tasks():
 
-    response = flask.jsonify({'tasks': [
-        {
-            'id': 1,
-            'title': 'my first todo'
-        }
-    ]})
+    response = flask.jsonify({'tasks': db.get_tasks()})
     response.status_code = 200
+    
+    return response
+
+@app.route('/api/v1.0/tasks', methods=['POST'])
+def add_task():
+
+    request = flask.request
+    
+    if not (request.json and 'title' in request.json):
+        flask.abort(400)
+
+    db.add_task(request.json['title'])
+    
+    response = flask.jsonify({'status':'ok'})
+    response.status_code = 201
+    
     return response
 
 if __name__ == '__main__':

edit myserver_test.py

diff --git a/myserver_test.py b/myserver_test.py
index 4f8da0d..b1dac21 100644
--- a/myserver_test.py
+++ b/myserver_test.py
@@ -1,3 +1,6 @@
+import os
+os.environ["TEST"] = 'True'
+
 import myserver
 import unittest
 import json
@@ -5,19 +8,26 @@ import json
 class MyServerTestCase(unittest.TestCase):
 
     def setUp(self):
-        myserver.app.config['TESTING'] = True
         self.app = myserver.app.test_client()
 
     def test_hello(self):
         rv = self.app.get('/')
         assert b'Hello, World' in rv.data
 
-    def test_get_tasks(self):
+    def test_task_api(self):
+        response = self.app.get('/api/v1.0/tasks')
+        rv = self.app.post(
+            '/api/v1.0/tasks', 
+            content_type='application/json', 
+            data=json.dumps({"title":"Hello"})
+        )
+
         response = self.app.get('/api/v1.0/tasks')
         assert response.status_code == 200
         assert response.headers['Content-Type'] == 'application/json'
         content_body_dict = json.loads(response.data.decode())
         tasks = content_body_dict['tasks']
+        assert len(tasks) is 1
         assert tasks[0].get('id') is not None
         assert tasks[0].get('title') is not None

test it

% python myserver_test.py
..
----------------------------------------------------------------------
Ran 2 tests in 0.017s

OK

check

run it

% python myserver.py

and check it

% curl http://localhost:5000/api/v1.0/tasks
{
  "tasks": []
}

% curl -i -X POST -H "Content-Type: application/json" -d '{"title":"title1"}' http://localhost:5000/api/v1.0/tasks
HTTP/1.0 201 CREATED
Content-Type: application/json
Content-Length: 20
Server: Werkzeug/0.9.6 Python/3.4.2
Date: Fri, 28 Nov 2014 04:27:19 GMT

{
  "status": "ok"
}

% curl http://localhost:5000/api/v1.0/tasks
{
  "tasks": [
    {
      "id": 1,
      "title": "title1"
    }
  ]
}

enjoy:)