Django REST framework 超入門
今回は Django で rest な API を実装するのに超絶便利な Django REST framework を紹介します。
その名の通り、Django で RESTful な API を作るための framework になります。
APIの開発効率が何倍にも上がる代物ですので、ぜひみなさまにも使っていただきたいと思っています。
はじめに
前回作成した racchai
プロジェクトをベースに進めていきます。
まずは rest_framework のインストールから。
$ pip install djangorestframework $ pip install markdown $ pip install django-filter
次は racchai/settings.py
の INSTALLED_APPS に rest_framework
を追記します。
# Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'racchai', + 'rest_framework', )
これで準備は整いました!
簡単なAPIを作ってみる
その前に、前回の記事で作成したモデル情報を改めて用意します。
$ cat > racchai/models.py <<EOF from django.db import models class Writer(models.Model): name = models.CharField(max_length=128) class Article(models.Model): writer = models.ForeignKey(Writer, related_name='articles') title = models.CharField(max_length=512) contents = models.TextField() EOF
ここまでは Django を使う場合と一緒です。
Serializer を定義する
rest_framework では、Serializer と呼ばれるモジュールを利用するシーンが多いです。
等々、用途は様々です。
Serializer を制するものが rest_framework を制します。
今回は各モデルに対応する Serializer として、ModelSerializer
を継承したクラスを作成します。
$ cat > racchai/serializers.py <<EOF from rest_framework import serializers from racchai.models import Article, Writer class WriterSerializer(serializers.ModelSerializer): class Meta: model = Writer class ArticleSerializer(serializers.ModelSerializer): class Meta: model = Article EOF
今回は WriterSerializer と ArticleSerializer を作成しました。
Articleを全件取得する API を作ってみよう
次は API を作成します。
viewsets.ModelViewSet
を継承して、serializer_class
に対応する serializer を設定します。
apis.py
$ cat > racchai/apis.py <<EOF from rest_framework import viewsets, routers from racchai.models import Article from racchai.serializers import ArticleSerializer class ArticleViewSet(viewsets.ModelViewSet): queryset = Article.objects.all() serializer_class = ArticleSerializer router = routers.DefaultRouter() router.register(r'articles', ArticleViewSet) EOF
なんとこれだけで API が実装できています。
では、urls.py で実際のエンドポイントを定義しましょう。
$ cat > racchai/urls.py <<EOF from django.conf.urls import url, include from racchai import apis urlpatterns = [ url(r'^api/', include(apis.router.urls)), ] EOF
これで準備が整いました。一瞬でしたね。
それでは開発サーバーを起動して、Article一覧を取得してみます。
$ ./manage.py runserver $ curl http://localhost:8000/api/articles.json [{"id":1,"title":"title1","contents":"contents1","writer":1},{"id":2,"title":"title2","contents":"contents2","writer":1}]
無事Article情報が取得できました!
前回の記事では Django 組み込みの JSON シリアライザを利用しましたが、非常にクセの強い JSON が生成されてしまいました。
rest_framework を使えば納得(普通)の出力結果を得ることができます。
API 経由でArticleを作成してみよう
実は、viewsets.ModelViewSet
を継承した時点で、レコード取得・作成・更新・削除までできるようになっています。
API 経由でデータを登録するには、POST
を使います。
$ curl -X POST http://localhost:8888/api/articles.json -d "writer=1&title=hoge&contents=fuga" {"id":4,"title":"hoge","contents":"fuga","writer":1}
なんか登録されてるっぽいですね!
再度全件取得してみましょう。
$ curl http://localhost:8000/api/articles.json [{"id":1,"title":"title1","contents":"contents1","writer":1},{"id":2,"title":"title2","contents":"contents2","writer":1},{"id":3,"title":"hoge","contents":"fuga","writer":1}]
新たに登録されたレコードが取得できているのがわかりますね。
Serializer の機能いろいろ
特定のフィールドは出力したくない
ありますよね。ユーザーのパスワードフィールドとかは画面には表示しない、とか。
出力対象のフィールドは Serializer クラス内で制限することができます。
手段はいくつかありますが、今回は writer フィールドを書き込み専用フィールドとして上書きする方法で実現してみます。
class ArticleSerializer(serializers.ModelSerializer): + writer = serializers.IntegerField(write_only=True) class Meta: model = Article
上のようにArticleSerializerに一行足して、レコード情報を取得してみましょう。
$ curl http://localhost:8000/api/articles/3.json {"id":3,"title":"hoge","contents":"fuga"}
無事、writer が表示されなくなりました。
外部キー参照先はキー値じゃなくてオブジェクトとして取得したい
今回でいう、writer はオブジェクトで取得したい!ということですが、これも簡単に実現が可能です。
class ArticleSerializer(serializers.ModelSerializer): + writer = WriterSerializer() class Meta: model = Article
writer フィールドを WriterSerializer
で上書きしてしまうだけです。
それでは確認してみます。
$ curl http://localhost:8000/api/articles/3.json {"id":3,"writer":{"id":1,"name":"racchai"},"title":"hoge","contents":"fuga"}
無事参照先レコードもオブジェクトとして取得できてますね。
外部キー参照先も同時に作成したい
参照がオブジェクトで取得できるなら、作成も同様にオブジェクト形式で送信したいですよね。
もちろん可能です。
ArticleSerializer
を以下のように編集しましょう。
class ArticleSerializer(serializers.ModelSerializer): writer = WriterSerializer() class Meta: model = Article + def create(self, validated_data): + writer = Writer(**validated_data.pop('writer')) + writer.save() + return super(ArticleSerializer, self).create(dict(validated_data, **{'writer': writer}))
やってることとしては、デフォルトの create 動作前に Writer を作成してるだけです。
では登録してみましょう。
今回はリクエストデータ自体も JSON にしてみます。
$ curl http://localhost:8000/api/articles.json -X POST -d "{\"writer\":{\"name\":\"new writer\"},\"title\":\"aaa\",\"contents\":\"bbb\"}" -H "Content-Type: application/json" │ {"id":4,"writer":{"id":2,"name":"new writer"},"title":"aaa","contents":"bbb"}
登録できてそうですね!
念のため登録したArticleを取得してみます。
$ curl http://localhost:8888/api/articles/4.json {"id":4,"writer":{"id":2,"name":"new writer"},"title":"aaa","contents":"bbb"}
作成時のレスポンスと同様のデータが取得できました。
まとめ
以上、簡単に Django REST framework について紹介してみました。
細かく解説できていない点も多いですが、簡単に API を実装できるイメージは伝わったかと思います。
本記事ではほんのさわりだけ紹介しましたが、まだまだいろんな機能が隠されています。
これからも、本ブログでは Django REST framework のノウハウについて紹介していきますので、お楽しみに!
5分で始める Django(入門編)
こんにちは!racchai です。
最近のマイブームは Django です。
本当に便利なのでみんなもっと使った方がいいのに!
ということで、簡単に導入方法などを書いていきます。
なお、これから出てくるコマンドおよびコード類は写経してたら5分で終わりませんので、コピペを推奨します。
はじめに
Django をインストールしましょう。
pip を使うのが簡単なので、合わせてインストールしちゃいます。
$ easy_install pip $ sudo pip install django
プロジェクトを作る
次にプロジェクトを作成するのですが、これは以下のコマンドを実行するだけ。 django-admin コマンドはさきほどの Django のインストールで入ったものです。
$ django-admin startproject racchai
たったこれだけでプロジェクトのひな形が自動生成されてしまいます。簡単ですね!
racchai プロジェクトに移動すると、直下に manage.py
というのがいるので、これを実行してみます。
$ ./manage.py Type 'manage.py help <subcommand>' for help on a specific subcommand. Available subcommands: [auth] changepassword createsuperuser [django] check compilemessages createcachetable dbshell diffsettings dumpdata flush inspectdb loaddata makemessages makemigrations migrate runfcgi shell showmigrations sql sqlall sqlclear sqlcustom sqldropindexes sqlflush sqlindexes sqlmigrate sqlsequencereset squashmigrations startapp startproject syncdb test testserver validate [sessions] clearsessions [staticfiles] collectstatic findstatic runserver
という具合に、サブコマンド一覧が表示されますので、この開発ツールを使って作業を進めていくわけですね。
例えば、開発サーバーを起動するときは
$ ./manage.py runserver
てな具合です。
データベースを作ってみよう
みなさんお待ちかねのデータベースの時間です。
racchai プロジェクト直下に、さらに racchai ディレクトリが作成されていますので、ここに models.py
なるファイルを作りましょう。
今回はライターさんと、そのライターさんが書いた記事を管理する感じで作ります。
$ cat > racchai/models.py <<EOF from django.db import models class Writer(models.Model): name = models.CharField(max_length=128) class Article(models.Model): writer = models.ForeignKey(Writer, related_name='articles') title = models.CharField(max_length=512) contents = models.TextField() EOF
次に上記が認識されるよう、racchai/settings.py
にて INSTALLED_APPS に racchai
を追加します。
# Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'racchai', )
準備ができたら、マイグレーションファイルを作成します。
$ ./manage.py makemigrations racchai Migrations for 'racchai': 0001_initial.py: - Create model Article - Create model Writer - Add field writer to article
マイグレーションファイルの作成に成功したら、あとはマイグレーションを実行すれば完了です。
$ ./manage.py migrate Operations to perform: Synchronize unmigrated apps: staticfiles, messages Apply all migrations: admin, contenttypes, racchai, auth, sessions Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying racchai.0001_initial... OK Applying sessions.0001_initial... OK
これでデータベースの作成が完了です!
データベースにアクセスしてみる
せっかくなので、Django の ORM 経由でデータベースを操作してみましょう。
まずはインタラクティブコンソールを立ち上げます。
$ ./manage.py shell
起動できたら全ライターを取得してみます。
>>> from racchai.models import Writer >>> Writer.objects.all() []
結果は0件! 何もデータが入っていないので当然ですね。
では何かデータを入れてみましょう。
>>> Writer(name='racchai').save() >>> Writer.objects.all() [<Writer: Writer object>]
今度は1件取得できました!
ちなみにどんな SQL が実行されているかを覗いてみることもできます。
>>> str(Writer.objects.all().query) 'SELECT "racchai_writer"."id", "racchai_writer"."name" FROM "racchai_writer"'
シンプル!
次は Article のデータも入れてみます。
>>> from racchai.models import Article >>> writer = Writer.objects.all().first() >>> Article(writer=writer, title='title1', contents='contents1').save() >>> Article(writer=writer, title='title2', contents='contents2').save() >>> Article.objects.all() [<Article: Article object>, <Article: Article object>]
Articleのデータを2件入れてみました。
そうなると、Article
の Writer
を取得したい、または逆に Writer
が所有する Article
の一覧を取得したい、のような操作がしたくなるのが世の常ですね。
ではやってみます。
ArticleのWriterを取得
>>> article = Article.objects.all().first() >>> article.writer <Writer: Writer object>
Writerが所有するArticleの一覧を取得
>>> writer = Writer.objects.all().first() >>> writer.articles.all() [<Article: Article object>, <Article: Article object>]
直観的で美しいインターフェイスですね!
API 経由でデータを出力してみる
せっかくなので、さきほど作ったデータを API で出力してみましょう。
以下の2ファイルを作成します。
views.py
$ cat > racchai/views.py <<EOF import json from django.http import HttpResponse from django.core import serializers from racchai.models import Article def show_articles(request): return HttpResponse(serializers.serialize("json", Article.objects.all())) EOF
urls.py
$ cat > racchai/urls.py <<EOF from django.conf.urls import url from racchai import views urlpatterns = [ url(r'^articles$', views.show_articles), ] EOF
/articles
へアクセスすることで、すべての Article のデータを JSON 形式で出力する準備ができました。
開発サーバーが起動できるかを確認してみます。
$ ./manage.py runserver
開発サーバーの起動に成功したら、/articles
へアクセスしてみましょう。
$ curl http://localhost:8000/articles [{"fields": {"writer": 1, "contents": "contents1", "title": "title1"}, "model": "racchai.article", "pk": 1}, {"fields": {"writer": 1, "contents": "contents2", "title": "title2"}, "model": "racchai.article", "pk": 2}]
おめでとうございます!
無事、クセが強い JSON レスポンスが取得できましたね!
まとめ
いかがでしたでしょうか。
今回は導入編ということで、データベースを作ってそのデータを出力するところまでをさらっと解説してみました。
Django だけでも非常に便利ですが、Django REST framework というフレームワークと組み合わせることで
API の開発をもっと楽に進めることができます。
次回以降はそのあたりに触れつつ、django 開発をする中で得られた知見などを共有していければなと思います。
それではまた!