REST API は、Web サービスがデータを送受信するための業界標準の方法です。
HTTP リクエストメソッドを使用してリクエストとレスポンスのサイクルを促進し、通常 JSON、まれに HTML、XML、その他の形式を使用してデータを転送します。
このガイドでは、Django を使って Python で REST API を作成し、 Django REST Framework を使ってショッピングカートアプリケーションを作成します。
を参照してください。
注意: このアプリケーションの完全なコードは GitHub で見ることができます。
REST APIとは?
REST (Representational State Transfer) は、Webサービスを構築し、通信するための標準的なアーキテクチャです。
一般に、Web上のリソースはテキスト形式(JSON、HTML、XMLなど)で表現され、所定の操作によってアクセスまたは変更できることが要求されます。
REST APIは通常、他のプロトコルではなくHTTPを活用するために構築されるため、これらの操作はGET、POST、PUTなどのHTTPメソッドに対応する。
API(Application Programming Interface)とは、その名が示すように、異なるソフトウェアコンポーネント間の相互作用を定義するインターフェースです。
Web API は、あるコンポーネントに対してどのようなリクエストを行うことができるか(たとえば、ショッピングカートのアイテム一覧を取得するエンドポイント)、どのようにリクエストを行うか(たとえば、GET リクエスト)、そしてどのようなレスポンスを期待するかを定義します。
このガイドでは、この 2 つの概念を組み合わせて、 Django REST Framework を使って REST(ful) API、つまり REST アーキテクチャスタイルの制約に従った API を構築します。
Django REST Framework とは何ですか?
Django REST Framework (DRF) は、Web API を作成するために Django の上に構築されたパッケージです。
Django の最も顕著な特徴の一つは、Python 的な方法でデータベースとの対話を容易にする ORM (Object Relational Mapper) です。
しかし、Python オブジェクトをネットワーク経由で送ることはできないので、 Django のモデルを JSON や XML のような他の形式に変換したり、その逆に変換したりするメカニズムが必要です。
シリアライゼーションとも呼ばれる、時に困難なこのプロセスは、 Django REST Framework を使えばとても簡単になります。
を参照してください。
注意: Django 自体で REST API を作る場合と、Django REST で作る場合の違いに注意する必要があります。
Django を使って古典的な Web アプリケーションを作り、その機能を REST API を使って世界に公開することができます。
実際、これはとても簡単なことです! しかし、Django REST Framework はこのタスクに特化しており、素の Django の上に構築されているので、このプロセスをより簡単にすることができます。
>
DjangoフレームワークでREST APIを作成する方法については、DjangoとPythonでREST APIを作成するガイドを参照してください。
をご覧ください。
Django とアプリケーションのセットアップ
Django は、Rapid Application Development (RAD) プロジェクト向け に作られています。
では、Django プロジェクトを素早くセットアップしてみましょう。
まず最初に、依存関係や他の依存関係への影響を整理するために、仮想環境を初期化し、 有効化しましょう。
$ mkdir drf_tutorial
$ cd drf_tutorial
$ python3 -m venv env
$ envscriptsctivate # Windows
$ . env/bin/activate # MAC or Linux
>
> virtualenv の詳細については、 Python 仮想環境解説! を参照してください。
をご覧ください。
>
> あるいは、virtualenvの代替品については、direnvとpyenvを使ったPython環境管理ガイドを読んでください!
をご覧ください。
そして、Django と Django REST Framework をその環境内にインストールします。
$ pip install django
$ pip install djangorestframework
最後に、 api_app
という名前のプロジェクトとアプリを作成します。
$ django-admin startproject shopping_cart
$ cd shopping_cart # Project contains app
$ python3 manage.py startapp api_app
アプリを作成したら、 settings.py
ファイルにアプリを登録する必要があります。
ここでは、管理機能や簡単な認証機能を提供する admin
や auth
などのビルトインアプリケーションと一緒に紹介します。
shopping_cart の下にある settings.py
ファイルを開き、作成した api_app
ディレクトリを INSTALLED_APPS
リストに追加してください。
また、Django に Django REST Framework (以下 DRF) を使用することを知らせるために、rest_framework
をリストに追加しましょう。
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api_app',
]
登録が完了したら、マイグレーションを適用し (データベースを初期化し)、データベースを監視するためのスーパーユーザーを作成します。
$ python3 manage.py migrate # Initialize database
$ python3 manage.py createsuperuser # Prompts for username and password
スーパーユーザーを作成し、アプリを登録したら、リクエストを受け付けるためにサーバーを起動します。
これは manage.py
内の runserver
コマンドで簡単に行うことができます。
$ python3 manage.py runserver
>
Django Web アプリケーションの生成、設定、構成のプロセスや、Django Web アプリケーションの基本コンポーネント (モデルの定義、モデルの登録、Django 管理インターフェースなど) は、 Core Django REST API Guide で詳しく説明されています。
DRFを使ってDjangoでREST APIを作成する
Django アプリの準備が整ったので、ドメインモデル、永続化、ビジネスロジックの開発を開始します。
ドメインモデル
ここでは、オンラインショッピングカートのアイテム(商品)を表す簡単なモデル CartItem
を作成しましょう。
api_app/models.py` ファイルで、モデルを定義します。
from django.db import models
class CartItem(models.Model):
product_name = models.CharField(max_length=200)
product_price = models.FloatField()
product_quantity = models.PositiveIntegerField()
定義したら、モデルを Django に登録し、管理パネルからアクセスできるようにします。
api_app/admin.py` に移動して、以下の行を追加します。
from django.contrib import admin
from .models import CartItem
admin.site.register(CartItem)
新しいモデルを定義したら、モデルをデータベースに反映させるために makemigrations
を行う必要があります。
コマンドプロンプトから、以下を実行します。
$ python3 manage.py makemigrations
$ python3 manage.py migrate
これでモデルが使えるようになりました。
Webアプリケーションでは、モデルのデータをあるエンドから別のエンドに転送することが頻繁にあります。
>
シリアライザーは、モデルの表現をJSON形式で定義し、オブジェクトのインスタンスをより転送しやすい形式に変換します。
これによって、APIのデータ解析が簡単になります。
デシリアライザーはその逆で、JSONデータをオブジェクトインスタンスとして我々のモデルに変換します。
私たちは、レスポンスを送信する前に、シリアライザーを使ってモデルオブジェクトをJSONに変換することにします。
そして、JSONのリクエストを受信すると、シリアライザーはそれをモデルオブジェクト(この場合は CartItem
)に変換します。
api_appフォルダに
serializers.pyファイルを作成し、モデル用の
ModelSerializer` を記述します。
from rest_framework import serializers
from .models import CartItem
class CartItemSerializer(serializers.ModelSerializer):
product_name = serializers.CharField(max_length=200)
product_price = serializers.FloatField()
product_quantity = serializers.IntegerField(required=False, default=1)
class Meta:
model = CartItem
fields = ('__all__')
models.pyファイルでは、モデルの
product_quantity` 属性を必須フィールドに設定しました。
これにより、オブジェクトを保存する際に、この属性が常に存在するようになります。
しかし、ユーザーが product_quantity
を指定していない場合、賢明なデフォルトの仮定は、ユーザーが1つのアイテムを購入したいことです。
API はこのような場合にエラーをスローせず、デフォルトで product_quantity
を 1
に設定する必要があります。
シリアライザーはこのシナリオを優雅に処理し、 views.py
でそのようなロジックを記述する必要はありません。
シリアライザークラスの属性に、必要なバリデーションやその他の制約を追加するだけでよいのです。
デフォルトでは、すべてのフィールドの required
は True
に設定されています。
したがって、シリアライザーはそれらを取得しない限り、処理を進めません。
APIViewクラス
純粋な Django と同様に、DRF では API に対してクラスベースのビューと関数ベースのビューの両方が可能です。
このガイドでは、クラスベースのビューを優先します。
このガイドでは、クラスベースのビューを優先します。
Django の View
クラスのサブクラスである APIView
クラスを使用してビューを表現します。
このようにして、ブートストラップされた post()
, get()
, patch()
, delete()
メソッドを手に入れれば、永続化レイヤーを全くいじらずに、 CartItem
モデルに対して簡単に CRUD 操作ができるようになります!(訳注:このメソッドは、Django の View
クラスのサブクラスです。
注意: すべての基本的なロジックをフレームワークに委ねることは魅力的ですが、このレイヤーを後日手動で操作する可能性があること、そしてデータベースに対する正しい理解が強く推奨されることは覚えておいて損はありません。
get()、
post()、
patch()、
delete()メソッドは、
all()、
save()、
delete()` などのモデルメソッドと同時に使用して、アプリのCRUD機能を容易にすることが可能です。
ビューを表す CartItemViews
クラスは、 APIView
を継承しています。
class CartItemViews(APIView):
...
エンティティの作成 – POSTリクエストハンドラ
POSTリクエストは、リクエストボディに含まれるデータをサーバーに送信するために使用されます。
これは新しいエンティティを作成するときに使用することを意図しています。
ビューに移動して、CartItemモデルのための
POST` リクエストハンドラを作成しましょう。
api_app/views.pyに移動して、
post()メソッドを持つ新しいクラスを作成し、
POSTリクエストボディを受け取ってバリデーションし、
CartItem` クラスのオブジェクトを DB に作成しましょう。
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import CartItemSerializer
from .models import CartItem
class CartItemViews(APIView):
def post(self, request):
serializer = CartItemSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
else:
return Response({"status": "error", "data": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
ここでは、まず、以前に作成した CartItemSerializer
を使用して、 request.data
から serializer
オブジェクトを作成していることがわかります。
is_valid()関数は、リクエストボディを使用して
CartItemオブジェクトを作成できるかどうかを示す
Boolean値を返します。
そして、save()メソッドは新しい
CartItem` のインスタンスを作成する。
レスポンスオブジェクトは、返されるデータで初期化されている必要があります。
このデータは bool
、str
、dict
など、任意の型の Python オブジェクトのインスタンスとすることができる。
>
その他のオプションのパラメータには、HTTP レスポンスコードを設定する status
や、 data
のレンダリング方法を示す content-type
、 HTMLRenderer
を選択した場合に使用できる template_name
、HTTP レスポンスで特定のヘッダーを送信する場合は headers
があります。
それでは、 post()
メソッドを使用するためのエンドポイントを設定し、公開しましょう。
これは shopping_cart/urls.py
を編集して、私たちのアプリが公開するエンドポイントを含めることで行います。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api_app.urls')),
]
エンドポイントが公開されたので、実際の CartItemViews
クラスをユーザー用のビューとして登録したいと思います。
これはGUIという意味でのビューを含んでいないことに注意してください – リクエストハンドラなのです。
ここでは、 api_app.urls
をインクルードし、ビューを接続するロジックを api_app
内の urls.py
スクリプトに委譲しています。
api_appフォルダに
urls.pyというファイルを新規に作成し、
cart-items/ロケータと
CartItemViews` クラスをリンクします。
from django.urls import path
from .views import CartItemViews
urlpatterns = [
path('cart-items/', CartItemViews.as_view())
]
path()の最初の引数はビューにアクセスするためのサブパスで、2番目の引数はリクエストを処理するために
views.py` で作成したクラス名です。
サーバーの起動
アプリを実行し、エンドポイント /api/cart-items/
を使用してみましょう。
$ python3 manage.py runserver
これはローカルサーバを http://127.0.0.1:8000/
で開始します。
別の端末で、エンドポイントに POST
リクエストを送信し、データを送信してみましょう。
$ curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/api/cart-items/ -d "{"product_name":"name","product_price":"41","product_quantity":"1"}"
ビューは受信したリクエストを処理し、製品のデータと status
を返します。
{
"status": "success",
"data": {
"id": 21,
"product_name": "name",
"product_price": 41.0,
"product_quantity": 1
}
}
シリアライザーはJSONデータを受け取り、それを具象オブジェクトにデシリアライズし、再びシリアライズしてレスポンスを返します。
http://127.0.0.1:8000/admin/api_app/cartitem/`にアクセスすると、先ほど追加したアイテムが見つかります。
また、http://127.0.0.1:8000/api/cart-items/
にアクセスすると、DRFのもう一つの顕著な特徴である、ブラウズ可能なAPIを見ることができます。
なお、ビューに関連するHTMLページは作成していませんが、DRFが自動生成してくれています。
Note: “Template Not Found “というタイトルのエラーメッセージが表示されたら、shopping_cart/settings.py
の INSTALLED_APPS
配列に rest_framework
が含まれているかどうか確認してください。
まだ CartItemViews
の GET
ハンドラを作成していないため、GET
メソッドは許可されないと書かれています。
しかし、入力フィールドがあるので、それにもかかわらずエンドポイントに POST
リクエストを送信することができます。
データ検証を依頼する
>
> 誰かが無効なデータを入力した場合はどうなりますか?例えば、product_quantity
属性に文字列を入力した場合、明らかに期待されるデータ型と一致しないのですが?
>
>
>
エンドポイント api/cart-items
に無効なリクエストをしてみましょう。
$ curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/api/cart-items/ -d "{"product_name":"name","product_price":"41","product_quantity":"One"}"
この場合、次のようなレスポンスが返ってきます。
{
"status": "error",
"data": {
"product_quantity": [
"A valid integer is required."
]
}
}
このエラーは serializer.errors
によって美しく表示され、 product_quantity
属性に有効な値を入力するように促されます。
モデルは何を期待しているかを知っていて、私たちは間違った型を提供しました。
これはDRFの素晴らしい機能で、自動的なデータ検証を行うことができます。
特にブートストラップやプロトタイピングの場合、単純な入力の検証という煩わしい作業から解放されます。
しかし、カスタムバリデータを使って、独自のバリデーションルールを定義することもできます。
>
バリデータについてもっと知りたい場合は、 Django バリデータガイド (近日公開!) を読んでください。
を参照してください。
エンティティの取得 – GET リクエストハンドラ
アイテムをカートに追加することに成功したので、そのエンティティを取得するロジックを定義してみましょう。
リソースを取得する方法には、2つの典型的な方法があります。
- カートにリンクされているすべてのエンティティをリストアップするために
GET
リクエストを作成することができます。 - URLパラメータとして
id
を渡すことで、カートから特定のエンティティを取得することができます。
モデルから特定のオブジェクトを取得し、 CartItemSerializer
を使用してそのデータをシリアライズすることができる。
同様に、モデル内のすべてのオブジェクトを取得して、そのデータをシリアライズすることもできます。
後者の場合、追加の引数 many
を渡す必要がある。
serializer = CartItemSerializer(items, many=True)
api_app/views.pyファイルを修正して、オブジェクトの
idと、
idが指定されていない場合はカートにある他の全てのアイテムを
GET` してみましょう。
...
class CartItemViews(APIView):
...
def get(self, request, id=None):
if id:
item = CartItem.objects.get(id=id)
serializer = CartItemSerializer(item)
return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
items = CartItem.objects.all()
serializer = CartItemSerializer(items, many=True)
return Response({"status": "success", "data": serializer.data}, status=status.HTTP_200_OK)
オプションの id
引数が省略された場合、特定のアイテムではなく、すべてのカートアイテムを返します。
どちらの場合でも、 Response
によってリクエストがどのようになったかをクライアントに知らせ、シリアライズデータが注入されます。
それでは、エンドポイントである api/cart-items/
に GET
リクエストを送信してみましょう。
$ curl -X GET http://127.0.0.1:8000/api/cart-items/
これは、次のように結果を取得します。
{
"status": "success",
"data": [
{
"id": 1,
"product_name": "name",
"product_price": 41.0,
"product_quantity": 1
}
]
}
見ての通り、 CartItemSerializer(items, many=True)
は JSON フォーマットでシリアライズされたデータ (オブジェクトのリスト) を返しています。
また、 api/cart-items/1/
のように、URL から id
引数を指定することもできます。
このように可変URLでエンドポイントを登録すると、DRFは自動的にパス変数をリクエストの引数に結びつけます。
次に、アプリの urls.py
を修正して、クラス CartItemViews
を指すパス – cart-items/<int:id>
を追加してみましょう。
この時点で、api_app/urls.py
は次のようになります。
from django.urls import path
from .views import CartItemViews
urlpatterns = [
path('cart-items', CartItemViews.as_view()),
path('cart-items/<int:id>', CartItemViews.as_view())
]
ここで、エンドポイント api/cart-items/1
を叩くと、変数 1
は get()
メソッドの id
引数に解決されます。
$ curl -X GET http://127.0.0.1:8000/api/cart-items/1
これは次のようなレスポンスになります。
{
"status": "success",
"data": {
"id": 1,
"product_name": "name",
"product_price": 41.0,
"product_quantity": 1
}
}
ここで、 CartItemSerializer(item)
は CartItem
インスタンスのデータを配列ではなく、単一の JSON オブジェクトとして返していることがわかります。
エンティティの更新 – PATCH リクエストハンドラ
これでカートにアイテムを追加したり取り出したりできるようになり、カートの状態を直接変更したり観察したりできるようになりました。
さて、カートの中にあるアイテムを更新するエンドポイントが必要です。
例えば、量を増やすなど。
オブジェクトを更新するためには、特定の id
をターゲットとした POST
リクエストを使用します。
そして、そのオブジェクトを取得し、更新して、同じ id
の下に保存することで、変更を持続させることができます。
しかし、通常は POST
リクエストを使用することはないでしょう (使用できるとしても)。
作成と更新のロジックを切り離すために、既存のリソースにパッチを適用して変更するために、 PATCH
リクエストを使用します。
>
APIViewクラスは
patch()関数を提供し、
PATCH` リクエストを処理し、データを更新します。
再び api_app/views.py
に戻り、以下のように PATCH リクエストハンドラを追加します。
...
class CartItemViews(APIView):
...
def patch(self, request, id=None):
item = CartItem.objects.get(id=id)
serializer = CartItemSerializer(item, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response({"status": "success", "data": serializer.data})
else:
return Response({"status": "error", "data": serializer.errors})
この行に注意してください。
serializer = CartItemSerializer(item, data=request.data, partial=True)
ここでは、シリアライザーに3つの引数を渡しています。
- 更新したい
CartItem
モデルのインスタンスです。 - リクエストから受け取ったデータ。
-
partial=True
は、モデルCartItem
のすべてのフィールドを含んでいないことを示します。
実際のインスタンスを渡す必要があるので、まずリソースを取得してから更新するために get()
関数を使用しなければなりません。
注意: 更新のためにリソースを取得するときは、リソースがそもそも存在することを確認するための検証ロジックを実行するのが最善です。
そして、更新を行うので、シリアライザーを検証してから保存します。
そろそろ api/cart-items/1
に PATCH リクエストを送り、アイテムを更新しましょう。
$ curl -X PATCH http://127.0.0.1:8000/api/cart-items/1 -H 'Content-Type: application/json' -d '{"product_quantity":6}'
この結果、以下のようになります。
{
"status": "success",
"data": {
"id": 1,
"product_name": "name",
"product_price": 41.0,
"product_quantity": 6
}
}
レスポンスには更新された数量が表示されました。
また、http://127.0.0.1:8000/admin/api_app/cartitem/1/change/
にアクセスすると、正常に更新されていることがわかります。
エンティティの削除 – DELETEリクエストハンドラ
>
私たちは通常、追加、追加、追加して、請求書が届くまで、いくつかの商品を複数個、ギフトとして購入したいことを思い出します。
このような場合、現実を直視して、本当に必要でないものをカートから削除し、ロジックを見直すことになります。
ユーザーは、誤って商品を追加したり、単に気が変わったりした場合に、特定の商品をカートから削除できるようにする必要があります。
カートから商品を削除するには、 delete()
関数を実装して、削除したいオブジェクトの id
を渡します。
そして、モデル自体に対して delete()
を呼び出すことで、モデルを永続化から削除することができます。
データと具象オブジェクトの間で変換が行われないので、この目的のためにシリアライザーを使う必要はありません。
削除されたエンティティ自体の情報は返さないので、 CartItem.objects.get()
の代わりに、 get_object_or_404()
関数を使用して、与えられた id
のオブジェクトが存在しない場合は自動的に 404
応答を返します。
>
この方法では、エンティティをシリアライズしてレスポンスとして送り返さなくても、エンティティが存在する (404
を返さなかった) かどうかを知ることができます。
api_app/views.pyに戻り、
delete()` メソッドを追加してみましょう。
...
from django.shortcuts import get_object_or_404
class CartItemViews(APIView):
...
def delete(self, request, id=None):
item = get_object_or_404(CartItem, id=id)
item.delete()
return Response({"status": "success", "data": "Item Deleted"})
新しいimport文を見逃さないようにしましょう オブジェクトを取得した後、 delete()
メソッドを呼び出すと、データベースからオブジェクトが削除されます。
それでは、カートから商品を削除してみましょう。
$ curl -X "DELETE" http://127.0.0.1:8000/api/cart-items/1
アイテムが存在する場合、この関数は次のようなレスポンスを返すはずです。
{
"status": "success",
"data": "Item Deleted"
}
アイテムが存在しない場合、レスポンスは以下のようになります。
{
"detail": "Not found."
}
http://127.0.0.1:8000/admin/api_app/cartitem/にアクセスしても、そのアイテムはもう存在しません。
結論
このチュートリアルでは、 Django REST Framework を使って Django で RESTful API を構築する方法を示しました。
Django プロジェクトを作成し、api_app
アプリケーションを追加しました。
そして、 CartItem
モデルと、モデルのシリアライズとデシリアライズを処理する CartItemSerializer
を作成しました。
モデルに対して CRUD 操作を行うために、クラスベースのビューである CartItemView
を追加した。
post()を使ってカートにアイテムを追加し、
get()を使ってすべてのアイテムや特定のアイテムを取得しました。
また、アイテムを更新するためにpatch()を作成し、カートからアイテムを削除するために
delete()` を作成しました。
このアプリケーションの完全なコードは GitHub で見ることができます。
を参照してください。