Django Ninjaとは?DRFとの違い・Schema・認証・非同期の実装を解説
Django Ninjaは、Pythonの型ヒントとPydanticでリクエストとレスポンスを定義し、OpenAPIドキュメントを自動生成するDjango向けのAPIフレームワークです。2026年8月時点の最新版は2026年3月18日公開の1.6.2で、依存関係はDjango>=3.1,<6.1とpydantic>=2.0,<3.0。つまり1.x系はPydantic 2系が必須であり、Pydantic 1系を前提にした2023年前後の解説記事のコードはそのままでは動きません。この記事では公式リポジトリのソースとドキュメントを一次情報として、Django REST Framework(DRF)との判断基準、Schemaの書き方、認証、非同期、そしてコミュニティフォークのDjango Shinobiまでを整理します。
まとめ
結論から示します。既存のDjangoプロジェクトに後付けでREST APIを足すなら、Django Ninjaは有力な選択肢です。INSTALLED_APPSへの登録もマイグレーションも不要で、urls.pyに数行足すだけで動き始めます。
| 観点 | Django Ninja 1.6.2 | DRF 3.17.1 |
|---|---|---|
| スキーマ定義 | Pydantic 2系のSchema/ModelSchema | Serializerクラス |
| 対応Django | 3.1以上6.1未満 | 4.2以上 |
| OpenAPI生成 | 3.1.0を標準出力 | 組み込みは非推奨/別パッケージ |
| 非同期ビュー | 標準対応 | 標準では非対応 |
| ライセンス | MIT | BSD |
押さえるべき優先順位は明快です。まずPydanticのメジャーバージョン、次に1.5.0で削除された旧APIが自分のコードに残っていないか。この2点を確認せずにバージョンを上げると、起動時に例外で止まります。以下の各章で、判断に必要な具体を順に説明します。
Django Ninja 1.6系の動作条件と現在の開発状況
Django NinjaはPython製WebフレームワークであるDjangoの上で動くAPIレイヤーです。Djangoのビューを置き換えるのではなく、既存のプロジェクトへ並列に追加できます。
PyPIが公開する1.6.2の依存宣言はDjango>=3.1,<6.1、pydantic>=2.0,<3.0です。Django側の最新版は2026年7月7日公開の6.0.7なので、現行のDjango 6.0系まで上限内に収まります。一方でPydanticの下限は2.0。1.x系にPydantic 1系を組み合わせる構成は存在しません。
開発は継続しています。GitHubのvitalik/django-ninjaは2026年8月時点でスター約9,200、最終プッシュは2026年7月8日、ライセンスはMITです。リリース履歴を見ると1.5.0が2025年11月13日、1.6.0が2026年3月12日、1.6.2が同3月18日と、数か月単位で更新されています。「開発が止まっている」という2024年頃の評価は、現在の更新履歴とは一致しません。
バージョンを上げる前に確認すべき破壊的変更は1.5.0に集中しています。NinjaAPIのcsrf引数が削除され、ModelSchemaのclass Config記法のサポートも削除されました。後者はソースのninja/orm/metaclass.pyで明示的に検出され、ConfigErrorとともに「use ‘Meta’ instead」というメッセージを送出します。古い記事のコードを貼り付けると、この例外でクラス定義の時点から失敗します。
Django REST Frameworkとの使い分けと移行の判断基準
シリアライザ層を持つDRFと型ヒントで完結するNinjaの差
両者の違いは機能の多寡ではなく、入出力を定義する場所です。DRFはSerializerクラスにバリデーションと変換の責務を集約します。Django Ninjaは関数の引数と戻り値の型注釈にそれを寄せ、Pydanticが検証と変換を担います。
この違いは行数に出ます。1エンドポイントあたり、DRFではSerializer、ViewSet、Routerの3要素を定義するのが定石ですが、Django Ninjaはデコレータ付き関数1つで完結。DRFの仕組みと使いどころを把握したうえで比べると、置き換えの是非を判断しやすくなります。
OpenAPIの扱いでは差がさらに開きました。Django Ninjaは追加パッケージなしでスキーマを出力する一方、DRFは組み込みのスキーマ生成機能そのものを公式ドキュメントが「deprecated」と宣言し、代替としてdrf-spectacularパッケージを推奨しています。スキーマ駆動で開発するなら、この差は初期設定の手間に直結します。
Django Ninjaを選ぶべきでない条件
判断を曖昧にせず言い切ります。次の条件に当てはまるなら、Django Ninjaへの移行は見送るべきです。
- DRFのSerializerに業務ロジックが数千行の規模で蓄積している場合。移行はSerializerの書き直しと等価で、Pydanticのバリデータへ移す作業量が機能追加を上回ります。
- DRFのブラウザブルAPIやトークン管理、パーミッションクラスを運用の前提にしている場合。Django Ninjaにこれらの互換実装はなく、自前で組み直すことになります。
- Pydantic 1系に固定された別ライブラリが同一環境に同居している場合。1.x系は
pydantic>=2.0を要求するため、依存解決の時点で衝突します。 - Django 3.2など古いバージョンに留まったままDRFの最新版を使いたい場合。DRF 3.17.1が
django>=4.2を要求する一方、Django Ninjaは3.1から動くため、この条件では両者の前提が逆転します。
逆に、新規のAPIエンドポイント群を既存Djangoアプリへ足すケースでは、DRFを導入するよりDjango Ninjaのほうが構成要素は少なく済みます。
インストールからエンドポイント公開までの最小構成
導入手順は2ステップです。ここで、古い解説に頻出する余計な設定を1つ捨ててください。公式ドキュメントのトップページには「You don’t have to add Django Ninja to your installed apps for it to work」と明記されています。INSTALLED_APPSに'ninja'を追加する手順は不要です。
pip install django-ninja
あとはurls.pyにAPIインスタンスを定義してマウントします。
# urls.py
from django.urls import path
from ninja import NinjaAPI
api = NinjaAPI()
@api.get("/hello")
def hello(request, name: str = "world"):
return {"message": f"Hello {name}"}
urlpatterns = [
path("api/", api.urls),
]
この時点で/api/hello?name=ninjaが動き、クエリパラメータnameは型注釈strに従って検証されます。ドキュメントUIも初期状態で生成されますが、その公開先と設定は後述します。
エンドポイントが増えたらRouterで分割します。1.6.0では「Idempotent Router」が導入され、同じルーターを複数のAPIインスタンスへ、あるいは同一API内へ複数回マウントできるようになりました。リリースノートによれば、デコレータ・認証・タグ・スロットリングの設定はマウント間で完全に分離されます。同じCRUDルーターを、公開APIと管理APIで別々の認証を与えて使い回せます。この版より前は複数マウント自体がエラーでした。
SchemaとModelSchemaによる入出力定義
ModelSchemaのMeta記法とfields指定の落とし穴
入出力の型はSchemaを継承して定義します。Djangoモデルから自動生成する場合はModelSchemaを使い、内側にMetaクラスを置いてmodelとfieldsを指定します。
from django.contrib.auth.models import User
from ninja import ModelSchema, Schema
class UserOut(ModelSchema):
class Meta:
model = User
fields = ["id", "username", "first_name"]
class UserIn(Schema):
username: str
password: str
@api.post("/users", response=UserOut)
def create_user(request, data: UserIn):
user = User(username=data.username)
user.set_password(data.password)
user.save()
return user
fields = "__all__"という書き方もありますが、公式ドキュメントはWarningとして非推奨にしています。理由は上の例のUserモデルが端的で、パスワードハッシュのような公開してはいけないフィールドまでレスポンスに載るためです。列挙する手間を惜しまず、出す項目を明示してください。
Metaサブクラス自体を書き忘れた場合は、ModelSchema class '...' requires a 'Meta' subclassというConfigErrorで停止します。エラー文言がそのまま対処法になっているので、移行は機械的に進みます。
バリデーション失敗時のレスポンス設計と例外ハンドラ
型注釈に合わない入力は、ハンドラへ到達する前にPydanticが弾き、HTTP 422を返します。この既定挙動を変えたい場合や、外部サービス起因の障害を専用のステータスで返したい場合は、api.exception_handlerデコレータで例外ごとの応答を定義します。
class ServiceUnavailableError(Exception):
pass
@api.exception_handler(ServiceUnavailableError)
def service_unavailable(request, exc):
return api.create_response(
request,
{"message": "Please retry later"},
status=503,
)
クライアント側のエラー処理を安定させるには、422と503のように意味の異なる失敗を別ステータスへ分けるのが実務上の最短経路です。すべてを500に丸めると、リトライ可否をクライアントが判断できなくなります。
認証クラスの選択とスロットリングの適用範囲
ninja.securityモジュールが公開している認証クラスは、APIキー系がAPIKeyCookie、APIKeyHeader、APIKeyQuery、HTTP認証がHttpBasicAuthとHttpBearer、Djangoセッション系がSessionAuthとSessionAuthSuperUserです。セッション系はdjango_auth、django_auth_is_staff、django_auth_superuserというインスタンスとしても提供されており、そのままauth引数へ渡せます。なおスタッフ判定のクラスSessionAuthIsStaffもimport自体は可能ですが、モジュールの__all__には収載されていません。使うならインスタンス側のdjango_auth_is_staffが無難です。
トークン認証はHttpBearerを継承してauthenticateを実装します。戻り値がNoneでなければ認証成功として扱われ、その値がrequest.authに入ります。
from ninja import NinjaAPI
from ninja.security import HttpBearer
from ninja.throttling import AuthRateThrottle
class TokenAuth(HttpBearer):
def authenticate(self, request, token):
if token == "supersecret":
return token
api = NinjaAPI(
auth=TokenAuth(),
throttle=[AuthRateThrottle("100/s")],
)
スロットリングはninja.throttlingのクラスを初期化済みオブジェクトとして渡します。DRFがクラスを渡す設計なのに対し、Django Ninjaはインスタンスを渡す点が違います。レートはrequests/time-unit形式で、単位は秒(s)、分(m)、時(h)、日(d)。secやminのような綴りも受け付けます。100/5mと100/300sは同義です。
上の例でAnonRateThrottleを併記していないのには理由があります。ninja/operation.pyの処理順は認証が先、スロットリングが後です。そしてAnonRateThrottle.get_cache_keyはrequest.authがNoneでないときNoneを返し、制限をかけません。API全体にauthを設定すると、未認証リクエストは認証段階で401になり、通過したリクエストには必ずrequest.authが入ります。結果としてAnonRateThrottleは一度も発火しません。匿名アクセスの制限が必要なら、認証を任意にしたエンドポイント側に個別指定してください。
用途の線引きも明確にしておきます。公式ドキュメントはこのスロットリングを、セキュリティ対策やブルートフォース・DoSへの防御とみなすべきではないと注記しています。レート判定がDjangoのキャッシュフレームワーク上の非アトミック操作であり、送信元IPの詐称にも対応できないためです。悪意ある攻撃はWAFやリバースプロキシなどアプリケーションの外側で止めてください。ブラウザアプリからJWTで認証する構成は、ReactとDjangoをREST APIで連携する際のJWT認証手順が参考になります。
非同期エンドポイントとASGI運用で満たすべき条件
Django Ninjaの非同期対応は、Django 3.1で入った非同期ビューの上に成り立っています。関数定義にasyncを付け、内部で非同期対応のライブラリを使うだけで非同期エンドポイントになります。
import asyncio
@api.get("/say-after")
async def say_after(request, delay: int, word: str):
await asyncio.sleep(delay)
return {"saying": word}
運用側の条件が2つあります。1つは実行環境で、UvicornやDaphneといったASGIサーバーが必要です。WSGIのままでは非同期の利点が出ません。もう1つはI/Oの中身で、標準のtime.sleepのような同期処理を関数内に残すとイベントループを止め、非同期にした意味が消えます。
効果が出るのは、外部APIの呼び出し待ち、データベース応答の待機、ディスクI/Oの待機といったネットワークとI/O律速の処理です。逆に、DjangoのORMを素朴に呼ぶ処理はクエリ本数の問題に支配されがちで、非同期化より先にN+1問題の解消に手を付けるほうが効きます。非同期化はレイテンシの原因を特定してから選ぶ手段です。
なお1.5.0では、同期の認証クラスと非同期オペレーションを組み合わせた際の不具合が修正されています。HttpBearerを同期で書きつつasync defのエンドポイントを併用する構成は、1.5.0以降を使ってください。
OpenAPI 3.1ドキュメントとSwagger UIの公開設定
ドキュメントは追加設定なしで生成されます。NinjaAPIの既定値はソース上でtitle="NinjaAPI"、version="1.0.0"、openapi_url="/openapi.json"、docs_url="/docs"。path("api/", api.urls)でマウントしていれば、スキーマは/api/openapi.json、UIは/api/docsに出ます。
出力される仕様バージョンは3.1.0です。これはソースのninja/openapi/schema.pyでキーopenapiに固定値として書かれており、openapi_extraから既存キーを上書きする経路もないため、3.0系へ落とす手段は用意されていません。OpenAPI 3.0.xを前提にしたコードジェネレータやAPIゲートウェイと連携する場合は、3.1系に対応しているかを先に確認してください。JSON Schemaの扱いが3.0と3.1で異なるため、古い生成ツールでは型が正しく解釈されないことがあります。
UIはSwaggerが既定で、Redocへ切り替えられます。本番環境でドキュメントを閉じたい場合は、該当URLにNoneを渡します。
from ninja import NinjaAPI, Redoc
api = NinjaAPI(
title="Sample API",
version="2.0.0",
docs=Redoc(),
)
# ドキュメントを公開しない場合
private_api = NinjaAPI(docs_url=None, openapi_url=None)
認証を設定したAPIでは、Swagger UIのAuthorizeボタンから入力したトークンが以後のテスト呼び出しに付与されます。HttpBearerを実装した時点でセキュリティスキーマもスキーマ定義へ反映されるため、UI側の追加設定は要りません。
コミュニティフォーク「Django Shinobi」の採否基準
Django Ninjaを調べていると、名前の似たDjango Shinobiに行き当たることがあります。これはpmdevita氏が公開しているDjango Ninjaのフォークです。リポジトリの説明文では「a community-focused fork of Django Ninja」と位置づけられ、コミュニティが求める機能と修正に焦点を当てるとしています。PyPIの最新版は2026年3月10日公開の1.4.2、GitHubのスターは約300、最終プッシュは2026年8月1日です。パッケージ名はdjango-shinobiですが、インポート名はninjaのままで、移行時にコードのimport文を書き換える必要はありません。
本家との差分で実務的に効くのは次の2点です。
- Schemaの処理性能改善。大きなデータを扱う場合の変換を高速化しますが、後方互換性が完全ではありません。
settings.pyのNINJA_COMPATIBILITYで切り替える設計で、1.4.0では互換モードが既定有効、高速化はオプトインです。公式のmigrationガイドは、1.5.0で既定値をFalseへ反転させ、1.6.0で互換動作そのものを撤廃する予定と記載しています。カスタムのmodel_validatorやfield_validatorを書いている場合は修正が要ることがあります。 - DjangoのChoicesとEnumの扱い。モデルフィールドで
.choicesを指定した場合、本家Ninjaは型を単なる文字列として扱いますが、ShinobiはModelSchemaとOpenAPIスキーマへ列挙型として引き継ぎます。OpenAPIからクライアントコードを自動生成している場合、この差は生成物の型定義に直接出ます。
採否の基準を明示します。標準は本家のDjango Ninjaです。理由はバージョンの追随状況にあり、Shinobi公式のmigrationガイドは「Shinobi 1.4.x is based on Ninja 1.4.3」と記載しています。Ninja 1.4.3の公開は2025年5月29日で、本家はその後1.5.0と1.6.0を出しています。つまりShinobiを選ぶと、Global/Router decoratorsやIdempotent Router、CursorPaginationといった本家の新機能は当面使えません。
動作条件も本家より狭くなります。django-shinobi 1.4.2の依存宣言はDjango>=4.2、pydantic>=2.5,<2.13、Python 3.9以上です。Django 3.2で運用しているプロジェクトは、この時点で候補から外れます。Pydanticの上限も2.13未満と本家より厳しく、他のライブラリとの同居で衝突しやすくなります。
Shinobiを選ぶ合理性があるのは、OpenAPI経由でクライアントを自動生成していてChoicesの列挙型が必要な場合と、Schemaの変換がプロファイリング上のボトルネックとして実測できている場合の2つに限られます。どちらにも当てはまらないなら、本家を使ってください。
よくある質問
Django NinjaはどのバージョンのDjangoとPythonで動きますか
1.6.2の依存宣言はDjango>=3.1,<6.1で、Django 3.1から6.0系までが対象です。Django最新版は2026年7月7日公開の6.0.7なので、現行版がそのまま上限内に収まります。Pythonはパッケージのメタデータが3.7以上を宣言していますが、Django 6.0自体がPython 3.12以上を要求するため、実際に組める下限は選ぶDjangoのバージョン次第。あわせてpydantic>=2.0,<3.0も必須です。
Django NinjaとDRFは同じプロジェクトで併用できますか
できます。Django Ninjaはurls.pyでapi.urlsをマウントするだけの構成で、DRFのルーターとは別のURLプレフィックスに同居させられます。既存のDRFで動いているエンドポイント群はそのまま残し、新規に追加するAPIだけをDjango Ninjaで書く進め方が現実的です。両者は依存パッケージも競合しません。ただし認証の実装は共有されないため、トークンの検証ロジックを二重に持たないよう、共通処理は別モジュールへ切り出してください。
Django Ninjaでページネーションはどう実装しますか
ninja.paginationのpaginateデコレータをエンドポイントに付けます。1.6.0ではCursorPaginationが追加され、オフセットではなくbase64エンコードしたカーソルトークンで位置を保持できるようになりました。件数の増減が頻繁なデータで、ページをまたぐ際に重複や取りこぼしが起きにくくなります。データが安定していてページ番号での移動が要件なら、従来のオフセット方式のままで問題ありません。
Django NinjaとFastAPIはどちらを使うべきですか
判断軸はDjangoを使うかどうかの一点です。Django NinjaはあくまでDjango上のレイヤーで、ORM・管理画面・認証といったDjangoの資産をそのまま使えます。FastAPIは2026年7月29日公開の0.141.1が最新で、依存はStarletteとPydanticのみ、Djangoとは独立したASGIアプリとして動きます。既にDjangoでモデルや管理画面を運用しているならDjango Ninja、Djangoの機能を使わない新規サービスならFastAPIです。型ヒントでスキーマを定義する書き味は両者でほぼ同じなので、この選択で学習コストは大きく変わりません。
Django Ninjaのバージョンを上げたら起動しなくなったのはなぜですか
1.5.0で削除された旧APIが残っている可能性が高いです。確認する箇所は3つあります。ModelSchemaのclass Configはclass Metaへ書き換えてください。NinjaAPIへ渡しているcsrf引数は削除します。環境にPydantic 1系を要求する別パッケージが残っている場合は、依存解決の時点で衝突し、そもそもインストールが完了しません。いずれも例外メッセージかpipの出力に原因が表示されます。