らっちゃいブログ

日々の学びと気づきを発信するブログ

Django REST framework 超入門

今回は Django で rest な API を実装するのに超絶便利な Django REST framework を紹介します。

その名の通り、Django で RESTful な API を作るための framework になります。

APIの開発効率が何倍にも上がる代物ですので、ぜひみなさまにも使っていただきたいと思っています。

はじめに

前回作成した racchai プロジェクトをベースに進めていきます。

racchai.hatenablog.com

まずは 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 と呼ばれるモジュールを利用するシーンが多いです。

  • リクエストパラメータのバリデーションおよびフィルタリング
  • データベースへの保存
  • jsonシリアライズ

等々、用途は様々です。

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件入れてみました。

そうなると、ArticleWriter を取得したい、または逆に 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 開発をする中で得られた知見などを共有していければなと思います。

それではまた!

racchai.hatenablog.com

racchai.hatenablog.com