PythonでDjangoを使ったREST APIを作成する

Django は、安全でスケーラブルな Web アプリケーションを少ない労力で迅速に構築するために使用される、強力な Python Web フレームワークです。

Django は、その低い参入障壁と、このフレームワークを使用し開発する強力なコミュニティにより、人気を博すようになりました。

このガイドでは、外部ライブラリなしで Django を使って RESTful API を構築するつもりです。

Django の基本をカバーし、ショッピングカートアプリケーションの CRUD 操作を実行する JSON ベースの API を実装します。

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つの概念を組み合わせて、REST(ful) APIを構築します。

RESTアーキテクチャスタイルの制約に従ったAPIです。

さっそく、Python と Django を使って作ってみましょう。

Django とアプリケーションのセットアップ

先に述べたように、Django は安全でスケーラブルな Web サービスの迅速な開発を促進する Web フレームワークです。

注意: 執筆時点の最新版である Django バージョン 3.1 を使用します。

Django をインストールする前に、念のため、そして依存関係を切り離すために、仮想環境を作っておきましょう。

$ python3 -m venv env


ある種のコードエディタでは、既に有効になっているのが見つかるでしょう。

もしそうでなければ、環境内の scripts ディレクトリに移動して、 activate を実行してください。

Windowsの場合。

$ envscriptsctivate


MacまたはLinuxの場合。

$ . env/bin/activate


それでは、pip を使って Django をインストールしましょう。

$ pip install django


インストールが完了したら、プロジェクトを作成します。

手動で作成することもできますが、 Django 自身でプロジェクトのスケルトンを作成する方がずっと便利です。

django-admin` ツールを使うと、空白のスケルトンプロジェクトをスピンオフして、すぐに作業を開始することができます。

このツールは Django 自身にバンドルされているので、追加インストールは必要ありません。

このツールを起動し、 startproject コマンドを実行し、その後にプロジェクト名を指定してプロジェクトを開始しましょう。

$ django-admin startproject shopping_cart


これで、作業ディレクトリに簡単なスケルトンプロジェクトが作成されます。

各 Django プロジェクトは複数のアプリを含むことができます – ただし、私たちは一つを作ることになります。

アプリケーションを起動するために、 startproject コマンドで作成した manage.py ファイルを呼び出してみましょう。

$ cd shopping_cart
$ python manage.py startapp api_app


一旦作成されると、プロジェクトの構造は以下のようになります。

> env
> shopping_cart
  > api_app
    > migrations
    __init__.py
    admin.py
    apps.py
    models.py
    tests.py
    views.py
  > shopping_cart
    __init__.py
    asgi.py
    settings.py
    urls.py
    wsgi.py
  manage.py


トップレベルの shopping_cart は Django のルートディレクトリで、プロジェクト名と同じ名前を持ちます。

ルートディレクトリはフレームワークとプロジェクトの橋渡しをし、アプリケーションを起動するために使用する manage.py のようないくつかのセットアップクラスが含まれています。

api_app`はスピンアップするアプリケーションで、ここには多くのアプリケーションを置くことができます。

それぞれのアプリケーションには、いくつかのデフォルトスクリプトがあり、CRUD機能を実現するために修正します。

低レベルの shopping-cart はプロジェクトディレクトリで、 settings.py のような設定関連のファイルがあり、私たちのアプリケーションの全てのプロパティを格納することができます。

Django は Model-View-Controller (MVC) です。

これは、アプリケーションを 3 つの構成要素に分離するデザインパターンです: 保存され対話されるデータを定義するモデル、データがユーザにどのように表示されるかを記述するビュー、そしてモデルとビューの間で仲介役として働くコントローラです。

しかし、このパターンに対する Django の解釈は、標準的な解釈とは少し異なります。

例えば、標準的な MVC フレームワークでは、ショッピングカートのアイテムを管理するための HTTP リクエストを処理するロジックは、コントローラに住むことになります。

Django では、そのロジックはビューを含むファイルに存在します。

その解釈については、こちらで詳しく説明されています。

コア MVC コンセプトと Django の解釈を理解することで、このアプリケーションの構造をより簡単に理解することができます。

各 Django プロジェクトには、いくつかの Django アプリケーション (モジュール) がプレインストールされています。

これらは、認証、認可、セッションなどに使われます。

私たち自身のアプリケーション api_app も含めたいことを Django に知らせるには、 INSTALLED_APPS リストにそれをリストアップする必要があります。

settings.py` ファイルにあるリストを修正し、私たちのアプリを含めるようにしましょう。

...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'api_app',
]


リストアップされたら、プロジェクトのセットアップは完了です。

しかし、私たちのアプリが構築される基盤はまだ完成していません。

CRUD 機能を開発する前に、基本的なデータ構造として動作するモデルが必要です。

注意: Django プロジェクトを開始すると、デフォルトでそのプロジェクト用の SQLite データベースも用意されます。

モデルを定義し、関連する CRUD 関数を呼び出すだけで、あなたのためにすべてを行うフードの下のプロセスが起動します。

モデルの定義

まずはシンプルで基本的なモデルから始めましょう – CartItem は、架空の eCommerce ウェブサイトに掲載されるアイテムを表します。

Django が拾い上げることのできるモデルを定義するために、 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()


ここでは、組み込みの db モジュールを利用していますが、このモジュールには models パッケージが含まれています。

Modelクラスは、まあ、モデルを表している。

このクラスは、CharFieldIntegerField` などの様々なフィールドを持っており、データベース内のモデルのスキーマを定義するために使用される。

これらは、モデルのインスタンスをデータベースに保存したいときに、Django の ORM によって、水面下でマッピングされます。

様々なフィールドが存在し、それらはリレーショナルデータベースでうまく動作するように設計されており、ここでもそうすることにします。

しかし、非リレーショナルデータベースの場合、データの保存方法が本質的に異なるため、あまりうまくいきません。

もし MongoDB のような非リレーショナルデータベースを使いたいなら、 Django MongoDB エンジンを使うためのガイド を参照してください。

先ほど行ったようなモデルスキーマの変更を行うには、 Django Migrations を呼び出す必要があります。

Migrations はとても簡単ですが、スキーマの変更を永続化したい場合は毎回実行する必要があります。

そのために、 manage.py ファイルを呼び出して、 makemigrationsmigrate を引数に渡します。

$ python manage.py makemigrations  # Pack model changes into a file
$ python manage.py migrate  # Apply those changes to the database


migrate` の操作は次のような結果になるはずです。

Operations to perform:
  Apply all migrations: admin, api_app, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying api_app.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 auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK


もし、migrations が正常に実行されなかった場合は、続ける前に、前のステップを見直して、正しくセットアップされていることを確認してください。

Django の管理サイト

アプリケーションを作成するとき、Django は自動的に admin サイトを作成します。

これは、開発者がテストしたり、登録したモデルに対して生成されたフォームにアクセスするために使うことを意図しています。

これは、開発中に便利なダッシュボードとして使うことだけを意図しており、実際の管理用ダッシュボードとしては使えませんので、管理用ダッシュボードが必要な場合は別途作成します。

django.contribadmin` モジュールは、管理サイトをカスタマイズするためのパッケージです。

Django の自動的なフォーム作成とモデル管理を利用するために、 admin.site にモデルを登録しなければなりません。

api_app/admin.py` に移動して、モデルを登録しましょう。

from django.contrib import admin
from .models import CartItem


admin.site.register(CartItem)


次に、このダッシュボードにアクセスして利用するユーザーを作成します。

Superadminアカウントを作成し、これらの変更が正常に行われたことを確認しましょう。

$ python manage.py createsuperuser


アカウントを作成するには、ユーザー名、メールアドレス、パスワードが必要です。

emailは空欄でOKです。

パスワードは入力しても反映されません。

そのまま入力してエンターキーを押してください。

Username (leave blank to use 'xxx'): naazneen
Email address:
Password:
Password (again):
Superuser created successfully.


最後に、アプリケーションを実行して、意図したとおりに動作しているかどうかを確認しましょう。

$ python manage.py runserver


アプリケーションはデフォルトで localhost (127.0.0.1) のポート 8000 で起動されます。

ブラウザで http://127.0.0.1:8000/admin にアクセスしてみましょう。

アプリケーションとデータベースモデルの設定が完了したので、次はREST APIの開発に専念しましょう。

DjangoでREST APIを作成する

Django アプリケーションの準備は完了です – 設定が定義され、アプリケーションが準備され、モデルが配置され、モデルが管理者ダッシュボードに登録されていることを確認するために管理者ユーザを作成しました。

では、モデルの CRUD 機能を実装してみましょう。

エンティティの作成 – POSTリクエストハンドラ

POSTリクエストはサーバーにデータを送信するために使用されます。

一般的には、保存されるはずのデータがボディに含まれています。

フォームに入力したり、画像をアップロードしたり、メッセージを送信したりするとき、POST` リクエストはそのデータと共に送信され、それに応じて処理され保存されます。

クライアントからデータを受け取り、モデルインスタンスにデータを投入し、データベースに追加する Django ビューを作成しましょう。

基本的に、私たちは API を使ってショッピングカートにアイテムを追加することができるようになります。

Django のビューは、純粋に関数として書くことも、クラスのメソッドとして書くこともできま す。

私たちはクラスベースのビューを使おうと思っています。

ビューを追加するために、 api_app_views.py ファイルを修正し、 POST リクエストを受け取る post() メソッドを追加します。

受信したリクエストのボディを辞書に書き込み、CartItemオブジェクトを作成して、データベースに保存します。

from django.views import View
from django.http import JsonResponse
import json
from .models import CartItem


class ShoppingCart(View):
    def post(self, request):


data = json.loads(request.body.decode("utf-8"))
        p_name = data.get('product_name')
        p_price = data.get('product_price')
        p_quantity = data.get('product_quantity')


product_data = {
            'product_name': p_name,
            'product_price': p_price,
            'product_quantity': p_quantity,
        }


cart_item = CartItem.objects.create(**product_data)


data = {
            "message": f"New item added to Cart with id: {cart_item.id}"
        }
        return JsonResponse(data, status=201)


jsonモジュールを使用して、受信したリクエストのボディをデコードして、作業可能なオブジェクトにパースし、データを変数p_namep_pricep_quantity` に抽出しました。

最後に、フィールドとその値を保持するために product_data 辞書を作成し、 Model クラスの create() メソッドによって CartItem をデータベースに登録し、 product_data にデータを格納しました。

最後に JsonResponse クラスを使用していることに注意してください。

このクラスを使って、Pythonの辞書を有効なJSONオブジェクトに変換し、HTTPでクライアントに送信しています。

サーバー側でリソースを作成したことを示すために、ステータスコードを201に設定します。

もし私たちがアプリケーションを実行し、このエンドポイントを叩こうとすると、 Django はセキュリティエラーでリクエストを拒否するでしょう。

デフォルトでは、 Django はクロスサイトリクエストフォージェリ (CSRF) 攻撃に対する防御のレイヤーを追加します。

実際には、このトークンはブラウザのクッキーに保存され、サーバへの全てのリクエストで送信されます。

この API はブラウザや Cookie なしで使用されるため、リクエストに CSRF トークンが含まれることはありません。

したがって、この POST メソッドは CSRF トークンを必要としないことを Django に伝えなければなりません。

これを実現するには、クラスの dispatch メソッドにデコレータを追加して、 csrf_exemptTrue に設定するようにします。

...
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt


@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCart(View):


def post(self, request):
        data = json.loads(request.body.decode("utf-8"))
        ...


これで、データを保存するモデルと、HTTPリクエストによって新しいカートアイテムを作成するビューができました。

あとは、Django に URL とそのハンドラをどう扱うか伝えるだけです。

アクセスされた各 URL に対して、それを処理する適切なビューマッピングを用意しま す。

各アプリにそれぞれの URL を書き、それをプロジェクトの urls.py ファイルにインクルードするのは、最初から全ての URL をトップレベルに持ってくるのではなく、グッドプラクティスだと考えられています。

まず、プロジェクトの shopping_cart ディレクトリにある urls.py を修正します。

from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('api_app.urls')),
]


デフォルトではインポートされていないので、 django.urls から include ライブラリをインポートしてください。

最初の引数は文字列のパスで、2つ目の引数はURLを取得する場所です。

パスが '' または空である場合、APIのURLはウェブアプリのルートパスになることを意味します。

次に、APIアプリの urls.py にエンドポイントを追加する必要があります。

api_appフォルダに、urls.py`という名前のファイルを新規に作成します。

from django.urls import path
from .views import ShoppingCart


urlpatterns = [
    path('cart-items/', ShoppingCart.as_view()),
]


プロジェクト自身の urls.py と同様に、最初の引数はビューにアクセスするためのサブパスで、2番目の引数はビュー自身です。

最後に、アプリケーションを実行します。

前と同じように、manage.py ファイルを使用して、runserver 引数を渡します。

$ python manage.py runserver


ターミナルを開いて、エンドポイントに POST リクエストを送信してみましょう。

Postman のような高度なツールから、 curl のようなシンプルな CLI ベースのツールまで、どんなツールでも使用することができます。

$ curl -X POST -H "Content-Type: application/json" http://127.0.0.1:8000/car
t-items/ -d "{"product_name":"name","product_price":"41","product_quantity":"1"}"


すべてがうまくいくと、メッセージが表示されます。

{
    "message": "New item added to Cart with id: 1"
}


http://127.0.0.1:8000/admin/api_app/cartitem/`にアクセスすると、追加したカートアイテムも表示されます。

エンティティの取得 – GET リクエストハンドラ

GETリクエストのハンドラを作ってみましょう。

これは通常、クライアントが何らかの情報を受け取りたいときに送信します。

データベースに保存されたCartItem` があるので、誰かがそれに関する情報を取得したいと思うのは理にかなっています。

複数のアイテムがある可能性を想定して、すべての CartItem エントリを繰り返し処理し、その属性を辞書に追加してみましょう。

では、ShoppingCartビューを修正してみましょう。

...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCart(View):


def post(self, request):
        ...


def get(self, request):
        items_count = CartItem.objects.count()
        items = CartItem.objects.all()


items_data = []
        for item in items:
            items_data.append({
                'product_name': item.product_name,
                'product_price': item.product_price,
                'product_quantity': item.product_quantity,
            })


data = {
            'items': items_data,
            'count': items_count,
        }


return JsonResponse(data)


count()メソッドはデータベース内の出現回数をカウントし、all()` メソッドはそれらをエンティティのリストに取り込みます。

ここでは、それらのデータを抽出し、JSONレスポンスとして返します。

エンドポイントに GET リクエストを送信してみましょう。

$ curl -X GET http://127.0.0.1:8000/cart-items/


この結果、クライアントに JSON 応答が返されます。

{
    "items": [
        {
            "product_name": "name",
            "product_price": 41.0,
            "product_quantity": 1
        },
    ],
    "count": 1
}


エンティティの更新 – PATCH リクエストハンドラ

API を使ってデータを永続化したり取得したりすることができますが、既に永続化されているエンティティを更新することも同様に重要です。

ここで登場するのが PATCHPUT リクエストである。

PATCHリクエストは、与えられたリソースを完全に置き換えます。

一方、PATCH` リクエストは指定されたリソースの一部を変更します。

PUTの場合、与えられたリソースコンテキストが存在しなければ、それを作成します。

PATCH リクエストを実行するには、リソースが既に存在している必要があります。

今回のアプリケーションでは、リソースが既に存在している場合にのみ更新したいので、 PATCH リクエストを使用することにします。

post()get()メソッドは、どちらも同じShoppingCart` ビュークラスにあります。

これは、これらのメソッドが複数のショッピングカートアイテムに影響を与えるからです。

PATCHリクエストは1つのカートアイテムにしか影響を与えません。

そこで、このビューと将来のdelete()メソッドを含む新しいクラスを作成することにします。

それでは、api_app/views.pyPATCH` リクエストハンドラを追加してみましょう。

...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCartUpdate(View):


def patch(self, request, item_id):
        data = json.loads(request.body.decode("utf-8"))
        item = CartItem.objects.get(id=item_id)
        item.product_quantity = data['product_quantity']
        item.save()


data = {
            'message': f'Item {item_id} has been updated'
        }


return JsonResponse(data)


与えられたIDのアイテムを取得し、それを修正してから再び保存することになります。

顧客が商品の価格や名前を変更できないようにしたいので、ショッピングカートの中の商品の数量だけをサイズ変更できるようにしています。

そして、save() メソッドを呼び出して、データベース内に既に存在する実体を更新しています。

さて、cart-items/ のエンドポイントにしたように、これにもエンドポイントを登録する必要があります。

api_app/urls.py`です。

from django.urls import path
from .views import ShoppingCart, ShoppingCartUpdate


urlpatterns = [
    path('cart-items/', ShoppingCart.as_view()),
    path('update-item/<int:item_id', ShoppingCartUpdate.as_view()),
]


今回は、HTTP動詞に頼るばかりではありません。

前回は、 /cart-itemsPOST リクエストを送ると post() メソッドが呼び出され、 GET リクエストを送ると get() メソッドが実行される結果となりました。

ここでは、URL 変数 – /<int:item_id を追加しています。

これは URL の動的なコンポーネントで、ビューから item_id 変数にマッピングされます。

指定された値に基づいて、適切なアイテムがデータベースから取得されます。

適切なデータを指定して、 http:127.0.0.1:8000/update-item/1PATCH リクエストを送信してみましょう。

$ curl -X PATCH http://127.0.0.1:8000/update-item/1 -d "{"product_quantity":"3"}"


この結果、JSONのレスポンスが返ってきます。

{
    "message": "Item 1 has been updated"
}


また、管理パネルで検証してみましょう。

http://127.0.0.1:8000/admin/api_app/cartitem/1/change/`。

エンティティの削除 – DELETEリクエストハンドラ

最後に、CRUD機能の最後のピースである、エンティティの削除です。

カートからアイテムを削除するには、1つのアイテムにしか影響しないので、同じ ShoppingCartUpdate クラスを使用します。

削除したいアイテムのIDを受け取る delete() メソッドをそれに追加します。

新しい値で更新するときにアイテムを save() するのと同じように、アイテムを削除するには delete() を使用します。

では、api_app/views.pydelete() メソッドを追加してみましょう。

...
@method_decorator(csrf_exempt, name='dispatch')
class ShoppingCartUpdate(View):


def patch(self, request, item_id):
        ...


def delete(self, request, item_id):
        item = CartItem.objects.get(id=item_id)
        item.delete()


data = {
            'message': f'Item {item_id} has been deleted'
        }


return JsonResponse(data)


そして、 DELETE リクエストを送信して、削除したいアイテムのIDを指定しましょう。

curl -X "DELETE" http://127.0.0.1:8000/update-item/1


そして、次のようなレスポンスが返ってくるでしょう。

{
    "message": "Item 1 has been deleted"
}


http://127.0.0.1:8000/admin/api_app/cartitem/` にアクセスすると、そのアイテムがもう存在しないことが確認できます。

結論

この短いガイドでは、Python で Django を使って REST API を作成する方法について説明しました。

Django の基礎のいくつかを説明し、新しいプロジェクトとその中のアプリを開始し、必要なモデルを定義し、CRUD 機能を実装しました。

このアプリケーションの完全なコードは、ここで見ることができます。

にあります。

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