Django で graphene-django を使って GraphQL API を実装

はじめまして!

colorful bullet の id:blt-horo です!

本記事はDjangoでGraphQLを実装する方法を紹介します。 何項かに分けて投稿していきますので、レベル感に合わせて読み進めてもらえればと思います!

What's GraphQL

GraphQLはFacebookによって開発されたOSSで、Web APIのクエリ言語です

graphql.org

GraphQLは「クエリ言語」と「スキーマ言語」から構成されます

「クエリ言語」

GraphQL APIのリクエストのための言語で、これはさらにデータ取得系のquery、データ更新系のmutation、サーバーサイドからのイベントの通知であるsubscriptionの3種類があります

「スキーマ言語」

GraphQL APIの仕様を記述するための言語で、リクエストされたクエリは、スキーマ言語で記述したスキーマに従ってGraphQL処理系により実行されて、レスポンスを生成します

What's graphene-django

GraphQLのためのPython製フレームワークに graphene があります その中のgraphene-djangoはDjangoプロジェクトにGraphQL機能を簡単に追加できるようにするためのライブラリです docs.graphene-python.org

ここまでで「GraphQL」「graphene-django」とは何かを簡単に紹介しました では、次項から実際に開発に入っていきましょう!

Github

この記事で使用した環境のプロジェクトはこちらです github.com

動作環境

Ubuntu 18.04 LTS
Python 3.7.3
Django 2.2
graphene-django 2.3.2

ディレクトリ構造

.
├── myproject
│   ├── __init__.py
│   ├── schema.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── myapp
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── fixtures
│   │   ├── sample.json
│   ├── migrations
│   │   ├── __init__.py
│   ├── models.py
│   ├── schema.py
│   ├── tests.py
│   └── views.py
└── manage.py

※仮想環境フォルダ等は省いています

プロジェクトフォルダ作成
$ mkdir myproject
$ cd myproject/
Python仮想環境作成
$ python3.7 -m venv  [仮想環境名]
$ source [仮想環境名]/bin/activate

仮想環境名はお好みで! ※python3.7-venvのパッケージを入れてない方は適宜インストールしてください
sudo apt-get install python3.7-venv

Djangoインストール

Djangoのインストール方法についてはこちらを参照ください

graphene-djangoインストール

pipでインストールします

$ pip install graphene_django
Djangoプロジェクト作成
$ django-admin startproject myproject .

アプリケーションの作成

サンプルアプリケーションを作成します

$ django-admin startapp myapp
設定の追加

プロジェクトの設定にgraphene_djangoとアプリのパスを追加します

# myproject/settings.py

# sample project application
INSTALLED_APPS += [
    'graphene_django', # 追加
    'myapp', # 追加
]

GRAPHENE = {
    'SCHEMA': 'myproject.schema.schema'
}

モデルの定義

myproject/myapp/models.pyに次の定義を追加します

# myproject/myapp/models.py

from django.db import models
from django.utils import timezone

class Account(models.Model):
    id = models.BigAutoField(primary_key=True)
    name = models.CharField(max_length=255, blank=True, null=True)
    password = models.CharField(max_length=500, blank=True, null=True)
    created = models.DateTimeField(blank=True, null=True)

    def __str__(self):
       return self.name

マイグレーション

以下を実行し、マイグレーションしておく

$ python manage.py makemigrations
$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
~~~
  Applying sessions.0001_initial... OK

スキーマの定義

myproject/myapp/schema.pyを作成し、次の定義を追加します

# myproject/myapp/schema.py

import graphene
from graphene_django.types import DjangoObjectType
from myapp.models import Account

class AccountType(DjangoObjectType):
    class Meta:
        model = Account

class Query:
    account= graphene.Field(AccountType, id=graphene.Int())
    all_accounts = graphene.List(AccountType)

    def resolve_account(self, info, **kwargs):
        id = kwargs.get('id')

        if id is not None:
            return Account.objects.get(pk=id)
        return None

    def resolve_all_accounts(self, info, **kwargs):
        return Account.objects.all()

また、プロジェクトのスキーマを作成し、次の定義を記述します

# myproject/myproject/schema.py

import graphene
import myapp.schema

class Query(myapp.schema.Query, graphene.ObjectType):
    pass

schema = graphene.Schema(query=Query)

エンドポイントの追加

URLを設定する

# myproject/myproject/urls.py

from django.conf.urls import include, url
from django.contrib import admin
from graphene_django.views import GraphQLView
from myproject.schema import schema

urlpatterns = [
    url(r'^graphql/', GraphQLView.as_view(graphiql=True, schema=schema)),  # エンドポイント
    url(r'^admin/', admin.site.urls),
]

url(r'^graphql/'のように記述するとhttp://localhost:8000/graphql/がAPIへのリクエストを受け付けるURLとなります また、graphiql=TrueとするとこのIDEであるGraphiQLの画面にアクセスすることができます

データの読み込み

サンプルデータを読み込むために以下のJSONファイルを作成します

# myproject/myapp/fixtures/sample.json

[{
 "model": "myapp.Account",
 "pk": 1,
 "fields": {
        "name": "apple",
        "password": "apple-pass"
 }
}, {
 "model": "myapp.Account",
 "pk": 2,
 "fields": {
        "name": "banana",
        "password": "banana-pass"
 }
}, {
 "model": "myapp.Account",
 "pk": 3,
 "fields": {
        "name": "orange",
        "password": "orange-pass"
 }
}]

下記コマンドでデータの読み込みをします $ python manage.py loaddata sample

Installed 3 object(s) from 1 fixture(s)

起動

$ python manage.py runsever

Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).
July 29, 2019 - 05:33:21
Django version 2.2.3, using settings 'myproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

起動が成功したら、http://127.0.0.1:8000/graphql/にアクセスしましょう

上記のようにGraphiqlにアクセスできると思います

GraphQLを書こう

GraphQLはクエリと結果として取得できるデータの構造が似ていることが特徴です さっそくクエリを書いてみましょう

Graphiqlの左側のペインに以下のクエリを入力します

query {
    account(id:1){ 
    id
    name
    password
  }
}

id = 1のデータを取得しています さっそく実行ボタンを押してみましょう すると以下の結果が返ってきます

{
  "data": {
    "account": {
      "id": "1",
      "name": "apple",
      "password": "apple-pass"
    }
  }
}

次にデータ全体を取得するクエリを入力します

query {
    allAccounts{ 
    id
    name
    password
  }
}

同じく実行ボタンを押すと、取得結果は以下となります

{
  "data": {
    "allAccounts": [
      {
        "id": "1",
        "name": "apple",
        "password": "apple-pass"
      },
      {
        "id": "2",
        "name": "banana",
        "password": "banana-pass"
      },
      {
        "id": "3",
        "name": "orange",
        "password": "orange-pass"
      }
    ]
  }
}

上記でも述べたように、クエリと結果の構造がとても似ています クエリの学習コストが小さくなるので便利ですね

終わりに

ここまででDjangoでGraphQLが使え、データ取得系のqueryも書けるようになりました

次回はデータ更新系のmutationを書いていきたいと思います

最後まで読んでいただきありがとうございました!


id:blt-horo さんの紹介記事

blog.bltinc.co.jp