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クラスは、まあ、モデルを表している。
このクラスは、CharFieldや
IntegerField` などの様々なフィールドを持っており、データベース内のモデルのスキーマを定義するために使用される。
これらは、モデルのインスタンスをデータベースに保存したいときに、Django の ORM によって、水面下でマッピングされます。
様々なフィールドが存在し、それらはリレーショナルデータベースでうまく動作するように設計されており、ここでもそうすることにします。
しかし、非リレーショナルデータベースの場合、データの保存方法が本質的に異なるため、あまりうまくいきません。
もし MongoDB のような非リレーショナルデータベースを使いたいなら、 Django MongoDB エンジンを使うためのガイド を参照してください。
先ほど行ったようなモデルスキーマの変更を行うには、 Django Migrations を呼び出す必要があります。
Migrations はとても簡単ですが、スキーマの変更を永続化したい場合は毎回実行する必要があります。
そのために、 manage.py
ファイルを呼び出して、 makemigrations
と migrate
を引数に渡します。
$ 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.contribの
admin` モジュールは、管理サイトをカスタマイズするためのパッケージです。
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_name、
p_price、
p_quantity` に抽出しました。
最後に、フィールドとその値を保持するために product_data
辞書を作成し、 Model
クラスの create()
メソッドによって CartItem
をデータベースに登録し、 product_data
にデータを格納しました。
最後に JsonResponse
クラスを使用していることに注意してください。
このクラスを使って、Pythonの辞書を有効なJSONオブジェクトに変換し、HTTPでクライアントに送信しています。
サーバー側でリソースを作成したことを示すために、ステータスコードを201に設定します。
もし私たちがアプリケーションを実行し、このエンドポイントを叩こうとすると、 Django はセキュリティエラーでリクエストを拒否するでしょう。
デフォルトでは、 Django はクロスサイトリクエストフォージェリ (CSRF) 攻撃に対する防御のレイヤーを追加します。
実際には、このトークンはブラウザのクッキーに保存され、サーバへの全てのリクエストで送信されます。
この API はブラウザや Cookie なしで使用されるため、リクエストに CSRF トークンが含まれることはありません。
したがって、この POST メソッドは CSRF トークンを必要としないことを Django に伝えなければなりません。
これを実現するには、クラスの dispatch
メソッドにデコレータを追加して、 csrf_exempt
を True
に設定するようにします。
...
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 を使ってデータを永続化したり取得したりすることができますが、既に永続化されているエンティティを更新することも同様に重要です。
ここで登場するのが PATCH
と PUT
リクエストである。
PATCHリクエストは、与えられたリソースを完全に置き換えます。
一方、PATCH` リクエストは指定されたリソースの一部を変更します。
PUTの場合、与えられたリソースコンテキストが存在しなければ、それを作成します。
PATCH リクエストを実行するには、リソースが既に存在している必要があります。
今回のアプリケーションでは、リソースが既に存在している場合にのみ更新したいので、 PATCH
リクエストを使用することにします。
post()と
get()メソッドは、どちらも同じ
ShoppingCart` ビュークラスにあります。
これは、これらのメソッドが複数のショッピングカートアイテムに影響を与えるからです。
PATCHリクエストは1つのカートアイテムにしか影響を与えません。
そこで、このビューと将来のdelete()メソッドを含む新しいクラスを作成することにします。
それでは、api_app/views.pyに
PATCH` リクエストハンドラを追加してみましょう。
...
@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-items
に POST
リクエストを送ると post()
メソッドが呼び出され、 GET
リクエストを送ると get()
メソッドが実行される結果となりました。
ここでは、URL 変数 – /<int:item_id
を追加しています。
これは URL の動的なコンポーネントで、ビューから item_id
変数にマッピングされます。
指定された値に基づいて、適切なアイテムがデータベースから取得されます。
適切なデータを指定して、 http:127.0.0.1:8000/update-item/1
へ PATCH
リクエストを送信してみましょう。
$ 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.py
に delete()
メソッドを追加してみましょう。
...
@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 機能を実装しました。
このアプリケーションの完全なコードは、ここで見ることができます。
にあります。