こんにちは、グローバルウェイの平田です。
API開発のフレームワークであるDjango Ninjaを使用した、POSTリクエストでデータを登録する実装方法についてご紹介します。
※Django Ninjaの概要とGETリクエストの実装は過去の記事で紹介しています。
この記事は以下の方を対象としています。
★4 Python開発経験が3年以上。
★3 Python開発経験が1年以上
★2 Python 初級者。簡単なプログラムコードが書けます。
★1 プログラミング未経験。
POSTリクエストでデータを登録
実装の例
まず、URLを設定します。
【urls.py】 from django.urls import path from ninja import NinjaAPI from api.views.views import router api = NinjaAPI() api.add_router(“/myapi”, router) urlpatterns = [ path(“v1/”, api.urls), ] |
リクエストのスキーマを定義することで、Pydanticのバリデーション機能を使用した入力チェックが利用できます。以下はPOSTで送信されてきたユーザー情報が期待通りか検証し、異なる場合は自動的にJSON形式のエラーレスポンスを返却します。
【request_schema.py】 from ninja import Schema from pydantic import constr, field_validator import datetime from ninja import NinjaAPI from datetime import date api = NinjaAPI() class UserRequestSchema(Schema): name: constr(min_length=1) phone_no: int email: str = None birth_date: date role: constr(min_length=1) is_active: bool |
例えば、以下はphone_noが数字に変換できない文字列だった場合のレスポンスになります。
422エラー { “detail”: [ { “type”: “int_parsing”, “loc”: [ “body”, “data”, “phone_no” ], “msg”: “Input should be a valid integer, unable to parse string as an integer” } ] } ] |
また、@field_validatorを使用すると、特定の項目のカスタムバリデーションの定義やデータの変換が可能となります。
例えばこちらは、roleが”admin”と”user”以外の場合はエラーを返却します。
@field_validator(“role”, mode=”before”) def validate_role(cls, role): if role not in [“admin”, “user”]: raise ValueError(“role not found”) return role |
以下のように日付をYYYY-MM-DDの形式に変換することも可能です。
@field_validator(“birth_date”, mode=”before”) def validate_birth_date(cls, birth_date): birth_date = datetime.datetime.strptime(birth_date, “%Y%m%d”).date() return birth_date |
バリデーションに成功したデータは、data(UserRequestSchema型のオブジェクト)に格納され、Userテーブルへ登録されます。
【views.py】 @router.post(“/users/”, response=UserResponseSchema) def create_user(request, data: UserRequestSchema): user = User( name=data.name, phone_no=data.phone_no, email=data.email or None, birth_date=data.birth_date, role=data.role, is_active=data.is_active, ) user.save() return user |
登録に成功した際に返却するレスポンスのスキーマを定義します。
【response_schema.py】 from ninja import Schema from ninja import Router from datetime import date from pydantic import constr router = Router() class UserResponseSchema(Schema): id: int name: constr(min_length=1) phone_no: int email: str = None birth_date: date role: constr(min_length=1) is_active: bool |
実際にAPIを実行してみます。以下のようなユーザー情報でリクエストしてみます。
http://localhost:8060/v1/myapi/users/{ “name”: “田中太郎”, “phone_no”: 123456789, “email”: “abc@test.co.jp”, “birth_date”: “20010101”, “role”:”user”, “is_active”: true } |
結果、ユーザー情報がテーブルに登録され、登録された内容がレスポンスとして返却されます。
200 OK { “id”: 6, “name”: “田中太郎”, “phone_no”: 123456789, “email”: “abc@test.co.jp”, “birth_date”: “2001-01-01”, “role”: “user”, “is_active”: true } |
テスト方法
Django NinjaはDjango標準のテスト機能を使用可能です。以下はTestCaseを継承して/users/エンドポイントに対してリクエストを送信し、ステータスコードとレスポンス内容を検証するテストコードの例です。
class UserRequestViewTest(TestCase): msg: str def test_user_request(self): client = TestClient(router) response = client.post(“/users/”, json=REQ_USER_DATA) res_user_data = response.json() del res_user_data[“id”] self.assertEqual(response.status_code, HTTPStatus.OK) self.assertEqual(res_user_data, RES_USER_DATA) |
リクエストデータ
REQ_USER_DATA = { “name”: “string”, “phone_no”: 123456789, “email”: “abc@test.co.jp”, “birth_date”: “20010101”, “role”: “admin”, “is_active”: True, } |
期待するレスポンスデータ
RES_USER_DATA = { “name”: “string”, “phone_no”: 123456789, “email”: “abc@test.co.jp”, “birth_date”: “2001-01-01”, “role”: “admin”, “is_active”: True, } |
まとめ
POSTリクエストを送信→データの検証→テーブル登録→レスポンス返却までの実装方法と、テスト方法について紹介しました。
今回の記事を読んでDjango Ninjaに興味を持たれた方は、参考文献に記載しているDjango Ninjaの公式ドキュメントをご覧いただき、さらに理解を深めてみてください。
参考文献