django-pistonでシンプルなAPIを作ってみる
最近django-tastypieをちょいちょい触っていたのですが、同じような目的のライブラリにdjango-pistonというのがあると知りました。
このサイトによるとtastypieとpistonで人気を二分しているように見えます。
http://www.djangopackages.com/grids/g/rest/
一番シンプルな使い方を試してみました。
まず普通のモデルを用意
app/models.py
class Todo(models.Model): title = models.CharField(max_length=255) finished = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True)
apiへアクセスに何を返すかをHandlerクラスに定義します
apiという別のappを作ってそこに書くのが流儀のようです
親クラスのBaseHandlerに基本の実装っぽいのが書いてあるんだけど、なんかよくわからない動きをするので、
参考にしながら意図通りのCRUD操作ができるように書いてみました。
api/handlers.py
from django.core.exceptions import ObjectDoesNotExist from piston.handler import BaseHandler from piston.utils import rc from todo.models import Todo class TodoHandler(BaseHandler): allowed_methods = ('GET','POST', 'PUT', 'DELETE') fields = ('id', 'title','finished', 'created') model = Todo def read(self, request, *args, **kwargs): pkfield = self.model._meta.pk.name if pkfield in kwargs: try: return self.queryset(request).get(pk=kwargs.get(pkfield)) except ObjectDoesNotExist: return rc.NOT_FOUND else: return self.queryset(request).all() def create(self, request, *args, **kwargs): attrs = self.flatten_dict(request.data) inst = self.model(**attrs) inst.save() return inst def delete(self, request, *args, **kwargs): if not args: for inst in self.queryset(request).all(): inst.delete() else: inst = self.queryset(request).get(*args, **kwargs) inst.delete() return rc.DELETED def update(self, request, *args, **kwargs): pkfield = self.model._meta.pk.name if pkfield not in kwargs: # No pk was specified return rc.BAD_REQUEST try: inst = self.queryset(request).get(pk=kwargs.get(pkfield)) except ObjectDoesNotExist: return rc.NOT_FOUND attrs = self.flatten_dict(request.data) for k,v in attrs.iteritems(): setattr( inst, k, v ) inst.save() return rc.ALL_OK
詳細はapi/urls.pyに書きます
api/urls.py
from api.handlers import TodoHandler from django.conf.urls import patterns from piston.resource import Resource todo_resource = Resource(TodoHandler) urlpatterns = patterns('', (r'^todo/(?P<id>\d+)/$', todo_resource), (r'^todo/$', todo_resource), )
あとsyncdbしとけば、でとりあえずアクセスできるようになりました。
requestsでひと通りアクセスしてみます
# coding=utf-8 import requests import json # データを全部削除 r = requests.delete('http://localhost:8000/api/todo/') assert(r.status_code == 204) # データが全て消えていることを確認 r = requests.get('http://localhost:8000/api/todo/') assert(r.status_code == 200) assert(len(json.loads(r.text)) == 0) # 登録 payload = {'title': u'test', 'finished': False} r = requests.post('http://localhost:8000/api/todo/', data=json.dumps(payload), headers={"Content-Type": "application/json"}) assert(r.status_code == 200) #全件取得 r = requests.get('http://localhost:8000/api/todo/') res = json.loads(r.text) assert(r.status_code == 200) assert(len(res) == 1) assert(res[0]["title"] == u"test") assert(res[0]["id"] == 1) ## idを指定して取得 r = requests.get('http://localhost:8000/api/todo/1') res = json.loads(r.text) assert(r.status_code == 200) assert(res["id"] == 1) assert(res["finished"] == False) assert(res["title"] == u"test") # 更新 payload = {'title': u'test', 'finished': True} r = requests.put('http://localhost:8000/api/todo/1', data=json.dumps(payload), headers={"Content-Type": "application/json"}) assert(r.status_code == 200) # 更新内容を確認 r = requests.get('http://localhost:8000/api/todo/1') res = json.loads(r.text) assert(r.status_code == 200) assert(res["id"] == 1) assert(res["finished"] == True) assert(res["title"] == u"test") # idを指定して削除 r = requests.delete('http://localhost:8000/api/todo/1') assert(r.status_code == 204) # データが消えていることを確認 r = requests.get('http://localhost:8000/api/todo/1/') assert(r.status_code == 404)
ちゃんと動きました。
感想
どっちがいいかはもっと使い込んでみないとなんともいえない。
ただとりあえず動かしてみるまではtastypieのほうが楽でした。
あと、pistonはbitbucket、tastypieはgithubなんだけど、みた感じtastypieのほうが盛んにcommitされているようです。
django-piston
https://bitbucket.org/jespern/django-piston/wiki/Homedjango-tastypie
https://github.com/toastdriven/django-tastypie