見出し画像

【サンプルコード・文法解説あり】Django REST Frameworkで自作API開発を習得

1. 初めに

こんにちは、ぎーたかです。

今回は、Django REST Framework(以下、DRF)で自作API開発する方法を解説します。

DRFとは、Djangoプロジェクトで自作API開発用に作られた拡張機能・パッケージです。サードパーティ製のパッケージですが、Djangoを使ったAPI開発では現在最も一般的に利用されているそうです。

ちなみにAPIとは、Application Programming Interfaceの略で”アプリとプログラムを繋ぐ”といった意味と捉えて良いです。自分の利用用途にあった他のアプリ/ソフトの機能をHTTPリクエストを通して利用できる点が特徴です。

今回の記事では、このDRFの概要を幅広く解説します。
DRFの特徴から触れ、そもそもAPIを使ってできることDRFで自作API開発のための文法知識をトピックとして取り上げます。

DRFの情報は、日本語で丁寧に解説されているものが少ないので、手を付けにくい方も多いと考え、記事化することにしました。
公式ドキュメントの情報は豊富ですけど、初学者からすると全て英語でキャッチアップするのはやっぱり辛いですよね。この記事がDRF初学者の導入の助けになればという思いで書いていますので、よければご活用ください。

今回の記事では「DRFでAPI作りたいけど、書き方がわからない」という方にとっても、基本的な文法を理解することができる様に書いていくので、DRF学習の起点として活用しやすい記事かと思います。

是非最後まで読んでいってくださると嬉しいです。
まずはDRFの特徴と、そもそもAPIでできることを解説していきましょう。

2. DRFの特徴・APIでできること

■DRFの特徴
まずDRFの特徴を解説しましょう。

DRFが出現した背景には、もともとDjangoがWebアプリフレームワークとしてAPI開発の面で機能不十分だったことがあります。Djangoの拡張機能として、REST API開発を効率化するために登場してきました。

ちなみに、RESTとはREpresentational State Transferの略称です。直訳しても直感的に理解しやすい意味ではないですが、一般にREST APIとはある条件を満たすAPIのことを指します。
その条件とは、例えば”HTTP通信で呼び出すことができる”とか、”ステートレスであること””処理結果がHTTPレスポンスコードで表現される”などがあります。

DRFは、こうしたREST APIのうち、モデルを使ったCRUD(作成(Create)・読み取り(Read)・更新(Update)・削除(Delete)の機能群のこと)処理が得意です。

これを踏まえたDRFの特徴としては、
API開発のための機能を標準で豊富に持っていること
・Djangoのモデル操作との連携を標準で簡潔に行えること
が挙げられ、REST APIはもちろんそれ以外のWEB APIの作成まで幅広くカバーして作れる点で非常に強力です。
こうした特徴を持つDRFを使えば、Djangoの文法+αの知識でどんなAPIも作れる様になります。

■APIでできること
ここまでDRFの概要を解説してきましたが、APIを作ってできること、つまりメリットについても解説しておきましょう。

◎メリット
利用者側・提供者側に分けてメリットを整理していきます。

<利用者側メリット>
・開発工数・コストの削減
 ⇨自分の作業、サービスでの利用目的にマッチするAPIがあれば、
  自分で開発せずとも常に最新の機能を利用できる。
・データの利活用
 ⇨自分の管理下にないデータも、APIを通して利活用できる。
・運用工数・コストの削減
 ⇨利用するAPIの運用・セキュリティの面倒は提供者側が見てくれる。

<提供者側メリット>
・収益化の可能性
 ⇨従量課金制のAPI化すれば、利用者から収益を得られる可能性もある。
・Webサービスのプログラムの拡張
 ⇨自作APIで非同期通信を必要とする作成物を作れたり、
  フロントエンドフレームワークでモダンなUIのページも作れたりする。

◎デメリット
APIは魔法ではないので、もちろんデメリットもあります。以下で一例を紹介しておきましょう。

<利用者側デメリット>
・APIバージョン依存のアップデート必要
 ⇨提供されるAPIレスポンスの変化により、
  自サービスでの取り入れ方を変えなければいけない時がある。

<提供者側デメリット>
・利用者にバージョン依存でアップデートさせないメンテナンスが必要
 ⇨アップデート時のユーザー体験が悪いと、ユーザー離脱の可能性高い。

DRF、APIの概要を色々と述べてきました。
APIを作るスキルは、近年のトレンドであるフロントエンドフレームワークでのサービス開発にはAPIが必須なため、今やAPI開発はWEB技術者としての市場価値を高める”習得しないと損なスキル”になってきています。

比較的簡単なWEB技術(HTML/CSS/JS)と比べて、技術難易度からしてもぐっと市場価値の高まるスキルの一つなので、そういった時代背景も理解しつつ、次の章からはサクッとDRFの基礎文法を学んでいきましょう。

次のパートからは、DRF未経験者向けに概要・処理イメージ・基本文法を最低限の範囲でなるべく分かりやすく紹介します。しかし、”最初からもっと体系的に全部学びたいんだ!”という方には以下の書籍をお勧めします。

Djangoの教科書シリーズのDRF版で、筆者はこちらを用いて学習しました。
DRFは通常のDjango以上にニッチな領域になるので、インターネット上に日本語で解説された知識が少ないです。
ましてや日本語の書籍なんて、”これ以外Amazonでも見たことない”というのが筆者の所感(2021年7月時点)ですので、もし興味があれば買って勉強してみるのも良いかと思います。

3. DRFの実装準備

このパートではDRFの実装準備について解説していきます。準備には、ライブラリインストール設定ファイルへの記述が含まれます。

なお、本記事での各ファイルへの記述は、以下のディレクトリ構造を前提に行なっていきますので、読者の皆さんが実践される場合は都度読み替えていただくようお願いします。

#本記事で前提にするディレクトリ構造
drf_sample            #プロジェクトルート
 |--config            #設定ディレクトリ
 |  |-__init__.py
 |  |-asgi.py
 |  |-settings.py     #DRFに関係
 |  |-urls.py
 |  '-wsgi.py
 |
 |--app               #アプリディレクトリ(WEBアプリ用)
 |  |-__init__.py
 |  |-admin.py
 |  |-apps.py
 |  |-models.py       #DRFに関係
 |  |-urls.py
 |  '-views.py
 |
 |--api               #アプリディレクトリ(API用)
 |  |-__init.py
 |  |-apps.py
 |  |-serializers.py  #DRFで重要
 |  |-urls.py
 |  '-views.py        #DRFで重要
...
 '-manage.py

■ライブラリインストール
まずはDRFライブラリをインストールしていきましょう。
pip(Python関連のライブラリ管理コマンド)を使ってインストールしていきます。

$ pip install djangorestframework

■設定ファイルへの記述
config/settings.pyへ以下の記述をしていきましょう。

...
INSTALLED_APPS = [
    ...
    'api.apps.SnsApiConfig',    #API用のアプリディレクトリ
    'rest_framework',           #DRFライブラリ
]
...

これでDRFのライブラリで提供される機能を、コード内で使う準備が整いました。

次のパートではDRFの機能のイメージを紹介します。
DRFの処理は、ブラウザで画面を表示するWEBアプリとして機能する場合とは異なりますので、混乱しないように先に処理の流れをイメージしておくと良いかと思い、解説します。
このパートでしっかりDRF脳を作って基礎文法を見ていけば、DRFを理解しやすくなること間違いなしですので、ぜひ習得していきましょう。

4. DRFの機能イメージ

ここまでDRFの概要をお伝えしてきて、機能の概要はなんとなくわかっていただけたかもしれませんが、”でも結局コーディングになったら、どれがどう機能してAPIになるのかさっぱりわからん”状態だと思います。

そのため、このパートではAP I実装方法の紹介の前にDRFのプログラム処理の全体像絵・イメージで理解しましょう。REST APIでモデル操作してレスポンスを出力する場合が例としてわかりやすく、かつ一般的なため、題材にして説明します。

■Django REST Frameworkの処理イメージ

DRF処理イメージ①

絵のリクエスト・レスポンスの流れを細分化して解説すると、以下の処理で構成されています。

◎DRFの処理ステップ
①ユーザーからのAPIへのリクエストで処理開始
②APIのメイン処理前に前処理があれば実行(例:認証で弾く、等)
③リクエスト時のURLからメイン処理の実行体であるAPIを呼び出し
④API実行体のレスポンスJSON型として設定された型を読み込み
⑤APIのコードに従い、モデル操作のCRUD処理実行
⑥モデル操作完了後、実行結果を設定したJSON型でレスポンスを返す

この処理の流れがイメージできたら、どのファイルでこれらの処理を分担しているかを理解しましょう。

◎DRF処理ステップとファイル分担(上述のディレクトリ構成を前提)
①⇨該当なし
②⇨config/settings.py
③⇨config/urls.py, api/urls.py
④⇨api/serializers.py
⑤⇨api/views.py
⑥⇨該当なし

図中に追記すると以下の様になります。

DRF処理イメージ②

ちなみにAPIでレスポンスにJSONが使われているのは、ここ最近は事実上の標準になっていて、理由としてはWebアプリで使われるJavaScriptと相性がいいためとされています。
そもそも"JSONって何?”という方向けに補足しておくと、オブジェクト表現方法の1つです。シンプルな階層構造で表現され、JavaScriptが他の言語とやりとりするために開発された方法です。

#JSONイメージ
{
    "name": "taro",
    "age": "30",
    "job": "engineer",
    "family": {
        "wife": "hanako",
        "son": "jiro",
    },
}

処理の概要を説明してきました。

DRFを使わないDjangoをある程度書ける方にとっては、DRFで作るAPIを作ると言っても、views.pyserializers.pyの書き方くらいしか、新たに学ぶ必要はありません。
これらはWebページ構築のためのDjangoでは使わない記法で書く必要がありますが、この2つさえ習得すればDRFの基礎は8割ほど理解できたといっても過言ではないです。

次のパートから、serializers.pyviews.pyの基本文法を解説していきます。

5. 基本文法(Serializer編)

レスポンスに使うJSON型の定義を記述していきます。
(リクエストデータの受け入れ側の型としても使うことあり)

このパートでは、app/models.pyファイルで以下のモデル定義がされている前提で解説していきます。

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
   title = models.CharField(max_length=50)
   content = models.TextField()
   user = models.ForeignKey(User, on_delete=models.CASCADE)
   like = models.ManyToManyField(User, related_name='related_post', blank=True)
   created_at = models.DateTimeField(auto_now_add=True)

それでは実装の方法を解説してきましょう。
他のファイルと同様に、1つのJSON型につき1つのクラス定義が必要になりますが、API処理で扱うモデルオブジェクトの数が単体か複数かに応じて、継承するクラスを変えて書く必要があります。

■モデルオブジェクト単体
まず、単体の場合から解説しましょう。
単体のモデルオブジェクトの操作用にはModelSerializerを継承したクラスで、serializerを書きましょう。ModelSerializerは単体のモデル操作用に作られた汎用クラスで、モデルに宣言されたフィールドだけをJSON型の項目に取り込みたいなら、非常に少ないコードで定義を完結できることもあります。

短くかける場合でどんなコードになるか、実際にお見せしましょう。以下のコードはapi/serializers.pyに記述します。

from rest_framework import serializers
from app.models import Post


class PostSerializer(serializers.ModelSerializer):

   class Meta:
       model = Post
       fields = ('id', 'title', 'content', 'user', 'like', 'created_at')

という様に、インポートを除けばたった4行で実装完了なこともあります。

構文も直感的で、ModelSerializerを継承したクラスの中で、JSON型の項目として”Post”モデルの”id”・"title"・"content"・"user"・"like"・"created_at"フィールドを使うことを宣言しています。
ModelSerializerを使うと、引用するモデルのフィールドタイプを自動的にシリアライザのフィールドタイプに変換してくれるので、機能を損なうことなくコード量を低減できる点で優れています。
(本来はserializers.CharField等で宣言が必要だが、models.CharField等の情報から適切に変換してくれる。)

結果、以下のJSON型をAPIレスポンスとできるシリアライザーの完成です。

#定義できたJSON型
{
    "title": "",
    "content": "",
    "user": "",
    "like": [],
    "created_at": "",
}

また、APIレスポンスにモデルの項目以外も含めたい場合のコードもお見せしておきましょう。この例では、モデルフィールドにはないis_staffのフィールドを追加しようとしています。

from rest_framework import serializers
from app.models import Post


class PostSerializer(serializers.ModelSerializer):

   #新規フィールドをメソッドの戻り値で作るフィールド追加
   is_staff = serializers.SerializerMethodField()

   class Meta:
       model = Post
       fields = ('id', 'title', 'content', 'user', 'like', 'created_at')

   #フィールド値を決定するメソッド    
   def_is_staff(self):
       return true if self.user=='administrator' else false

この例で使用したserializers.SerializerMethodFieldは、他のフィールドの状態によって状態を変化さえたい時に有用な追加フィールドです。宣言されたフィールドは、同クラス内に作られたget_フィールド名のメソッドの戻り値を格納するフィールドとして機能します。

基本はモデルにもフィールドを追加して、シリアライザーで引用する形を取れば良く、モデルに追加できない場合の対処方法として汎用的な手段になるかと考え、紹介しました。

■モデルオブジェクト複数
次に複数の場合を解説しましょう。
複数のモデルオブジェクトの操作用には、ListSerializerを継承したクラスを使いましょう。こちらも実際にどんなコードになるか、お見せしましょう。

from rest_framework import serializers
from app.models import Post


#複数オブジェクト扱う場合でも、その単体オブジェクト用シリアライザーは必要
class PostSerializer(serializers.ModelSerializer):

   class Meta:
       model = Post
       fields = ('id', 'title', 'content', 'user', 'like', 'created_at')


#child属性に単体オブジェクトのシリアライザーインスタンスを割り当て、完成
class PostListSerializer(serializers.ListSerializer):

   child = PostSerializer()

ListSerializerを継承したクラスでは、child複数化したいオブジェクトの単体シリアライザーをインスタンスで割り当てる必要があります。
ListSerializerを使って、以下のJSONをAPIレスポンスとして返せる型を作れました。

#定義できたJSON型
[
    {
        "title": "",
        "content": "",
        "user": "",
        "like": [],
        "created_at": "",
    },
    {
        "title": "",
        "content": "",
        "user": "",
        "like": [],
        "created_at": "",
    },
    ...
]

以上でserializers.pyの基本文法の解説は終了です。
いかがでしたか?まだ、この段階では作成したserializers.pyは、どのAPIにも繋がっていないので、実装だけ見ても作動がイメージできない部分があったかと思います。
views.pyの実装の中で、serializers.pyとの連携がよりイメージしやすくなるかと思うので、引き続きviews.pyの実装方法を確認していきましょう!

6. 基本文法(View編)

views.pyでAPIでメインに行う処理を定義します。

実装の方法を解説していきましょう。
さまざまな書き方がありますが、APIの機能がモデルオブジェクトのCRUD操作の書き方を中心に解説していきます。
DRFには、CRUD操作用APIのための豊富な汎用ビュークラスがあります。

■CRUD操作を全て実装したい場合
まず初めに、あるモデルのCRUD操作を全て実装したい場合、ModelViewSetを使うと簡潔にコードをかけます。"5. 基本文法(Serializer編)"で解説したapp/models.py・api/serializers.pyが存在することを前提に、ModelViewSetを使ったapi/views.pyのファイルは以下の様に書けます。

from rest_framework import viewsets
from app.models import Post
from .serializers import PostSerializer

#ModelViewSetを継承したビュークラスを定義
class PostViewSet(viewsets.ModelViewSet):

    queryset = Post.objects.all()        #querysetに対象モデルを宣言
    serializer_class = PostSerializer    #serializer_classに入出力に使うJSON型として宣言

処理対象のモデルをqueryset(Django特有の変数型、モデルオブジェクトのリストだと考えてOK)で、使用するJSON型をserializer_classで記述さえすれば最小機能の実装は完了です。

この書き方をする際は、urls.pyの書き方も簡潔に書くことができる記法があるので紹介します。以下のコードをapi/urls.pyに記述します。

from django.urls import path, include
from rest_framework import routers
from .views import PostViewSet

#Router(URLの自動登録機能を備えたインスタンス)を作成し、/posts系のAPIを一気に作成
router = routers.DefaultRouter()
router.register('posts', PostViewSet)

#RouterのルートURLのみいつものurlpatternsで宣言
urlpatterns = [
   path('', include(router.urls)),
]

この書き方で以下の6つのAPI機能(一般的なCRUD操作一式)が、urlpatternsに追加したURLを起点にしたURL × HTTPリクエストメソッドの組み合わせで割り当てられます。

上記のコードの場合は、
・/posts × get…対象モデルオブジェクトの全件読み取り
・/posts × post…対象モデルオブジェクトの1件作成
・/posts/<int:pk> × get…該当するIDのモデルオブジェクトの読み取り
・/posts/<int:pk> × put…該当するIDのモデルオブジェクトの更新
・/posts/<int:pk> × patch…該当するIDのモデルオブジェクトの一部更新
・/posts/<int:pk> × delete…該当するIDのモデルオブジェクトの削除
という機能を持つAPIを紹介したURLに実装できたことになります。

■一部のCRUD操作のみ実装したい場合
しかし、モデルによってはCRUD操作を全て実装する必要はない場合もあるでしょう。そういった場合はModelViewSetで作られるAPIビューを1つずつ実装するのに役立つ汎用ビューを使いましょう。

以降のビューではURLの割り当ては、通常のWEBブラウザで確認できるアプリとしてのDjangoの記法と同じやり方で構いません。

◎全件読み取り
ListAPIView
が使えます。

from rest_framework import generics
from app.models import Post
from .serializers import PostSerializer

class PostListAPIView(generics.ListAPIView):

    queryset = Post.objects.all()
    serializer_class = PostSerializer

ModelViewSetと同様、クエリセットとシリアライザーを指定すれば最小限の実装は完了です。
呼び出せば、モデルオブジェクトの全件の情報をシリアライザー定義に合わせたリストを、APIのレスポンスとして取得できます。

◎作成
CreateAPIView
が使えます。まずはコードを見てみましょう。

from rest_framework import generics
from app.models import Post
from .serializers import PostSerializer

class PostCreateAPIView(generics.CreateAPIView):

    serializer_class = PostSerializer

シリアライザーのみを指定すれば最小限の実装は完了です。
呼び出せば、呼び出し時に送付したJSON情報から、serializer定義のJSON型とのバリデーションを行い、型にマッチしていればその情報に合わせてモデルオブジェクトを作成、データベースに保存できます。
APIのレスポンスとしては、作成されたオブジェクトの情報が返ります。

◎読み取り
RetrieveAPIView
が使えます。

from rest_framework import generics
from app.models import Post
from .serializers import PostSerializer

class PostRetrieveAPIView(generics.RetrieveAPIView):

   queryset = Post.objects.all()
   serializer_class = PostSerializer

ModelViewSetと同様、クエリセットとシリアライザーを指定すれば最小限の実装は完了です。
呼び出せば、URLパラメータで指定したIDを持つモデルオブジェクトの情報をシリアライザー定義に合わせた形式で、APIのレスポンスとして取得できます。

◎更新
UpdateAPIViewが使えます。

from rest_framework import generics
from app.models import Post
from .serializers import PostSerializer

class PostUpdateAPIView(generics.UpdateAPIView):

   queryset = Post.objects.all()
   serializer_class = PostSerializer

ModelViewSetと同様、クエリセットとシリアライザーを指定すれば最小限の実装は完了です。
呼び出せば、URLパラメータで指定したIDを持つモデルオブジェクトの情報を、呼び出し時に送付したJSON情報から更新できます。送付されたJSONはserializer定義のJSON型とでバリデーションされ、型にマッチしていればその情報に合わせてモデルオブジェクトを更新、データベースに保存できます。
APIのレスポンスとしては、更新されたオブジェクトの情報が返ります。

◎削除
DestroyAPIView
が使えます。

from rest_framework import generics
from app.models import Post
from .serializers import PostSerializer

class PostDestroyAPIView(generics.DestroyAPIView):

   queryset = Post.objects.all()

クエリセットさえ指定すれば最小限の実装は完了です。
呼び出せば、URLパラメータで指定したIDを持つモデルオブジェクトの情報を削除、データベースを更新できます。
なお、APIのレスポンスはシリアライザーを指定しなければ返りませんが、指定すれば削除されたオブジェクトの情報が返ります。

どうでしたか?
ここまでDRFの肝になるserializers.pyとviews.pyの基本的な文法について解説してきました。紹介したコード自体は簡潔で、習得も難しくないものばかりだったのではないでしょうか。
本記事の冒頭でも説明した通り、DRFはAPI開発の豊富な機能が特徴なため、このように短く見通しの良いコードを書ける点が利点です。

読者の皆さんの中で、もし”DRFでAPI作ってみたいな”と感じてくださった方がいれば、まずはぜひ自分のWEBアプリ内で何らかの非同期処理(ページ遷移・リロードなしで、データベース操作を行い画面に変化を加えられる様な処理)を行うためのAPIを作って叩いてみる処理を組み込んでみることをお勧めします。
もし、SNSアプリを作ってらっしゃる方がいれば、まずは投稿への”いいね”をリロードなく付けられる機能の組み込みを検討してみてはいかがでしょうか。

なお、Djangoマガジンには↓こんな記事もありますので、ぜひポートフォリオ作りに活用してみてください!

7. API作成の周辺知識

このパートでは、前パートまでで紹介したAPIの作り方に+αして、実際にAPIを活用・確認する際の周辺知識について補足をしていきます。

■活用...自分のWebアプリ内でAPIを叩く方法
JavaScriptファイル内で叩く方法がいくつかあります。
その中でも簡単な例としては、jQueryajaxメソッドを使うことでしょうか。使い方、文法もろもろCODEPREPで学べるので、気になる方は以下に参考記事を貼っておきます。

ちなみに、React・VueなどのJavaScriptフレームワークでもAPIを叩く手段がありますが、こちらは別途記事を発行して解説したいと思います。

■確認...BrowsableAPI
APIの開発を始めると、少し変更してレスポンスを確認して、また変更して...という繰り返しになる場面があります。DRFには、そのレスポンスチェックを簡単に行える機能があります。
開発環境用のBrowsableAPIというものがあります。これはDRF導入直後からデフォルトで有効な機能で、実装したAPIをブラウザで確認できる機能です。
開発サーバーを立ててlocalhost:8000から、API用に割り当てたURLをブラウザで表示させると、ブラウザ上でAPIのレスポンスが期待通りになっているかわかりやすく確認できます。

以下は、Postモデルに1件のテスト投稿を追加して、Postモデル全件取得用のAPIのURLに開発サーバーでアクセスした際の表示です。この様な形で、想定通りのレスポンスが出ているかをすぐにチェックできる点が魅力です。

画像3

8. 終わりに

かなりボリューミーな内容になってしまいましたが、DRFの解説記事いかがでしたか?!今後、DRFをバックエンドに使ったフロントエンドフレームワークを扱う記事も書いていきますので是非そちらも読んでみてほしいです!

DRFの処理のイメージは理解することができましたか?
DRFにおいては、serializers.pyの担う役割自体(JSONとモデルオブジェクトのインターフェース)が理解しにくいので、とっつきにくかったかもしれません。
ただ、記述自体は決して多くはないので、本記事で紹介したDRFの処理イメージの絵に役割に照らし合わせれば理解しうる内容ばかりだったのではないでしょうか。

もし、DRFを習得してみたいという方は実際に自分のWEBアプリに組み込んでみましょう。まずは、ご自身で実装してみて、そのレスポンスを確認しながら開発の過程を理解していくのが良いかと思いますので、ぜひトライして見てください!

近年、APIは比較的簡単なWEB技術(HTML/CSS/JS)と比べると、非常に市場価値を高めてくれるスキルの1つになっていると筆者は考えています。
近年のトレンドの一つとして、フロントエンドもフレームワーク化するのが主流のため、ReactVueでのページ構築がかなり一般的に浸透してきています。

React・Vueのアプリでデータベース処理までやらせようと考えるなら、それに必要なAPIを作成する技術が必要になるため、WEB技術者として技術の幅を広げようと考えるならAPI開発スキルは”習得しないと損なスキル”になってきています。

ぜひこの記事で学んでくださったことを生かして、皆さんのWebエンジニアライフがより豊かになることを心から応援しています!それではまた次の記事でお会いしましょう!

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

よろしければサポートをお願いします🙇‍♂️ いただいたサポートは、クリエイターの活動資金として使わせていただきます😌 活動を通してえた経験を、また記事として皆さんと共有します👍