Django RESTフレームワークでREST APIを作成する

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 ファイルにアプリを登録する必要があります。

ここでは、管理機能や簡単な認証機能を提供する adminauth などのビルトインアプリケーションと一緒に紹介します。

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_quantity1 に設定する必要があります。

シリアライザーはこのシナリオを優雅に処理し、 views.py でそのようなロジックを記述する必要はありません。

シリアライザークラスの属性に、必要なバリデーションやその他の制約を追加するだけでよいのです。

デフォルトでは、すべてのフィールドの requiredTrue に設定されています。

したがって、シリアライザーはそれらを取得しない限り、処理を進めません。

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` のインスタンスを作成する。

レスポンスオブジェクトは、返されるデータで初期化されている必要があります。

このデータは boolstrdict など、任意の型の Python オブジェクトのインスタンスとすることができる。

>
その他のオプションのパラメータには、HTTP レスポンスコードを設定する status や、 data のレンダリング方法を示す content-typeHTMLRenderer を選択した場合に使用できる 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.pyINSTALLED_APPS 配列に rest_framework が含まれているかどうか確認してください。

まだ CartItemViewsGET ハンドラを作成していないため、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 を叩くと、変数 1get() メソッドの 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 で見ることができます。

を参照してください。

タイトルとURLをコピーしました