Django REST Framework(DRF)は、DjangoでWeb APIを作るための追加ライブラリです。素のDjangoでもJSONは返せますが、入力の検証・モデルへの保存・認証・ページ分割を毎回手で書くと、エンドポイントごとに書き方がばらつきます。DRFはこの定型処理を部品にまとめ、モデル1つなら20行ほどでCRUD APIが動きます。この記事では、2026年10月時点の最新版3.18.3で最小構成を組み、実際の実行結果をもとに「既定のままでは本番に出せない設定」と「3.18で変わった挙動」まで整理します。
まとめ:DRFの要点と3.18.3で押さえること
- DRFはDjangoの上に載るAPI用ライブラリ。PyPIのパッケージ名は
djangorestframework、コードで使う名前はrest_framework。 - 最新は3.18.3(2026年10月6日)。対応はDjango 5.2・6.0・6.1、Python 3.10〜3.15。3.18.0でDjango 4.2〜5.1の対応を外した。
- 最小構成はSerializer(変換と検証)→ ViewSet(処理)→ Router(URL生成)。これでURL2本に6アクションが揃う。
- 既定の権限は
AllowAnyで、認証なしのPOSTも201で通る。本番前にDEFAULT_PERMISSION_CLASSESを必ず設定する。 - 3.18.0から
many=Trueの検証エラーが「インデックスをキーにした辞書」に変わった。フロントエンドがエラーを配列で読んでいるなら上げる前に直す。 - 複数のCRUD APIに認証・権限・ページングを共通の設定で揃えたいならDRF。型ヒントでリクエストを定義したい、非同期ビューが要るならDjango Ninjaも候補。
Django REST Frameworkとは:DjangoにAPI層を足すライブラリ
DRFは、HTTPリクエストをPythonのデータに、Pythonのデータ(モデルのインスタンスなど)をJSONに変換する層をDjangoに足します。読み方は「ディー・アール・エフ」。モデル・ORM・認証基盤はDjangoのものをそのまま使うので、既存のDjangoプロジェクトにAPIだけを追加する用途に向きます。Django自体の仕組みはDjangoとは?特徴とMVTの仕組み・始め方をDjango 6.1で解説で扱っています。
DRFが部品にしている処理
素のDjangoでAPIを書くと、json.loadsでボディを読み、型を確かめ、保存し、エラー時のJSONを組み立てる処理をビューごとに書くことになります。DRFはこれを次の部品に分けています。
| 部品 | 担当 | 解説の場所 |
|---|---|---|
| Serializer | 変換と入力検証 | Serializerの詳細 |
| APIView / ViewSet | リクエストの受け口 | 本記事 |
| Router | URLの自動生成 | 本記事 |
| Authentication / Permission | 誰か/何をしてよいか | 認証連携の実装 |
| Pagination / Filter | 一覧の分割と絞り込み | 検索フィルタの実装 |
| Renderer | 出力形式(JSON・HTML) | 本記事 |
DRFで最も設計が分かれるのはSerializerで、ネストした関連データや独自の検証はここに集まります。フィールド定義・バリデーション・ネストの書き方はDjango REST FrameworkのSerializer(シリアライザ)とは?使い方・フィールド・バリデーション・ネストを解説に分けています。
3.18.3の動作要件と保守の方針
PyPIのdjangorestframeworkによると、2026年10月28日時点の最新は3.18.3で、requires_pythonは3.10以上、分類にはDjango 5.2・6.0・6.1とPython 3.10〜3.15が並びます。公式のリリースノートでは、3.18.0(2026年8月7日)でDjango 4.2・5.0・5.1の対応を終了し、3.17.0(2026年3月18日)でPython 3.9の対応を終えています。Django 4.2 LTSのまま運用しているプロジェクトは、DRFを3.17系で止めるか、Djangoを5.2 LTS以降へ上げるかの二択です。Django側のサポート期限はDjangoのEOLとサポート期限一覧【2026年10月】LTSとPython対応・移行判断で確認できます。
同じリリースノートには「REST framework is considered feature-complete」とあり、今後のリリースは不具合修正と互換性対応が中心になる方針です。3.18.2(2026年10月6日)では、巨大なAcceptヘッダへの対策など3件のセキュリティ修正(GHSA-33wh-fxxf-88vvほか)が入りました。ただし3.18.0でも検索フィルタのアクセント無視(unaccent)対応のような機能追加は入っているので、3.x.0に上げるときはリリースノートの変更点を読んでから上げます。
DRFで最小のREST APIを作る手順
ここではBookモデル(title・author・pages)のCRUD APIを、DRF 3.18.3+Django 6.1.2で組みます。前提は、django-admin startproject config .とpython manage.py startapp booksでプロジェクトとアプリを作り、booksをINSTALLED_APPSに登録した状態です。以下の出力は、この構成をPython 3.13で実行した結果です。
pip install djangorestframeworkとINSTALLED_APPSへの登録
pip install djangorestframework==3.18.3
インストールする名前はdjangorestframeworkですが、settings.pyに書くのはrest_frameworkです。ハイフン付きのpip install django-rest-frameworkでも入りますが、これは作者が2017年に登録した別名パッケージ(0.1.0・中身は本体への依存だけ)を経由してdjangorestframeworkを引き込む形です。requirements.txtには本体名と版を書いておくと、依存の記録が二重になりません。
INSTALLED_APPS = [
# 既存のアプリ
"rest_framework",
]
この登録が無いと、ブラウザブルAPIのテンプレートが読み込めずHTML表示でエラーになります。
Serializer・ViewSet・Routerの接続構成
まずAPIで扱うモデルを定義し、テーブルを作ります。
# books/models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
pages = models.PositiveIntegerField()
python manage.py makemigrations books
python manage.py migrate
続いて、Serializer・ViewSet・Routerの3ファイルをbooksアプリに置きます。
# serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ["id", "title", "author", "pages"]
# views.py
from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.order_by("id")
serializer_class = BookSerializer
# books/urls.py
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r"books", BookViewSet)
urlpatterns = router.urls
アプリのURLは、プロジェクト側のurls.pyからincludeでつなぎます。
# config/urls.py
from django.urls import include, path
urlpatterns = [
path("", include("books.urls")),
]
querysetにorder_byを付けているのは、後でページネーションを有効にしたとき、並び順の無いクエリセットだとDjangoがUnorderedObjectListWarningを出すためです。
DefaultRouterが生成するURLと6つのアクション
上のrouter.urlsから主要なパターンを抜き出すと、次のとおりです。このほかにapi-rootのフォーマットサフィックス付きパターンも生成されます。
^books/$ book-list
^books\.(?P<format>[a-z0-9]+)/?$ book-list
^books/(?P<pk>[^/.]+)/$ book-detail
^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ book-detail
api-root
books/が一覧(GET)と作成(POST)、books/{pk}/が取得・更新(PUT)・部分更新(PATCH)・削除の受け口です。ModelViewSetがこの6アクションを持ち、DefaultRouterがURLに割り当てます。.jsonのような拡張子付きのURLと、APIの入口ページ(api-root)も同時に作られます。入口ページが不要ならSimpleRouterを使います。
読み取り専用にしたいならReadOnlyModelViewSet、エンドポイントごとに処理を細かく書きたいならAPIViewか関数ベースの@api_viewを選びます。CRUDが定型的な資源はViewSet、外部APIの呼び出しや集計など独自処理が中心の箇所はAPIView、と分けると読みやすくなります。
APIClientによる正常入力・検証エラーの確認
開発サーバーを起動しなくても、DRF付属のAPIClientで呼べます。以下はマイグレーション適用後のpython manage.py shellで実行し、開発用設定ではALLOWED_HOSTSにtestserverを追加してください。掲載出力はBookテーブルが空で、英語のエラーメッセージを使う環境での例です。
from rest_framework.test import APIClient
c = APIClient()
r = c.post("/books/", {"title": "A", "author": "x", "pages": 100}, format="json")
print(r.status_code, r.content.decode())
r = c.post("/books/", {"title": "", "pages": "abc"}, format="json")
print(r.status_code, r.content.decode())
201 {"id":1,"title":"A","author":"x","pages":100}
400 {"title":["This field may not be blank."],"author":["This field is required."],"pages":["A valid integer is required."]}
検証エラーはフィールド名をキーにした辞書で返り、ビュー側に検証コードは1行もありません。モデルのCharFieldとPositiveIntegerFieldから、ModelSerializerが必須・空文字不可・整数の検証を組み立てています。
レスポンスの整形:JSONの字下げと出力形式の指定
DRFの既定のRendererはJSONRendererとBrowsableAPIRendererの2つです。ブラウザで開くとHTMLの操作画面、Accept: application/jsonを送るとJSONが返ります。実行すると、出力は次のように切り替わりました。
| リクエスト | Content-Type | 中身 |
|---|---|---|
| Accept: application/json | application/json | 1行のJSON |
| Accept: application/json; indent=2 | application/json | 2字下げのJSON |
| ?format=json | application/json | 1行のJSON |
| Accept: text/html | text/html | ブラウザブルAPI(検証例:10,300バイト) |
| ?format=api | text/html | ブラウザブルAPI |
確認用にJSONを読みやすく整形したいときは、サーバー側を変えずにAcceptへindent=2を付けるだけで足ります。
curl -H "Accept: application/json; indent=2" http://127.0.0.1:8000/books/1/
{
"id": 1,
"title": "A",
"author": "x",
"pages": 100
}
本番では、ブラウザブルAPIを外してJSONだけを返す設定にしておくと、フォームからの書き込み操作をブラウザに見せずに済みます。
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
],
}
レスポンスの項目名や構造(例:created_atを日付だけにする、関連先を名前で返す)を変えたいときは、RendererではなくSerializerのフィールド定義で調整します。
本番前に変える既定設定:権限・認証・ページネーション
DRFはREST_FRAMEWORKを何も書かなくても動きます。ただし、その既定値は「開発中に試しやすい」側に倒れています。この章以降の設定例は、settings.pyの1つのREST_FRAMEWORK辞書へ項目を足していく前提で書いています。例ごとにREST_FRAMEWORK = {...}を書き直すと、先に入れた権限やRendererの設定が消えて既定に戻るので注意してください。以下は公式のSettingsに載っている既定値を、節ごとにその設定だけを入れて実行した結果です。
既定のAllowAnyと匿名POSTの許可
DEFAULT_PERMISSION_CLASSESの既定はAllowAnyです。前章のコードでは、ログインしていないAPIClientからのPOSTが201で保存されました。PUTやDELETEも同じく通ります。社内向けの管理APIでも、URLが外から届くなら次のどちらかを最初に入れます。
REST_FRAMEWORK = {
# 書き込みも読み取りもログイン必須
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
],
# 読み取りを公開する場合は、上のリスト内のIsAuthenticatedを
# rest_framework.permissions.IsAuthenticatedOrReadOnlyに置き換える
}
IsAuthenticatedにすると、同じ匿名POSTは{"detail":"Authentication credentials were not provided."}で拒否されました。ビューごとに変えたい場合は、ViewSetにpermission_classesを書くと全体の既定より優先されます。
未認証時の401・403と認証クラスの順序
既定の認証はSessionAuthenticationとBasicAuthenticationの2つです。この既定のままIsAuthenticatedで拒否されると、ステータスは403でした。TokenAuthenticationだけにすると、同じ要求が401になり、WWW-Authenticate: Tokenヘッダが付きました。
公式のAuthenticationには「The first authentication class set on the view is used when determining the type of response」とあり、先頭の認証クラスがヘッダを返せるかどうかで401か403かが決まります。SessionAuthenticationはWWW-Authenticateを返さないため403になります。SPAやモバイルアプリから「未ログインなら401でログイン画面へ」と分岐させる設計なら、トークン系の認証を先頭に置きます。ReactからJWTで呼ぶ構成はReactとDjangoの連携方法|DRF・CORS・JWT認証の実装手順と本番構成で手順を追っています。
PageNumberPaginationとPAGE_SIZEの設定
PAGE_SIZEの既定はNoneです。以下のPageNumberPaginationを使い、クラス側でもページサイズを指定しない場合は、ページネーションが無効になります。DEFAULT_PAGINATION_CLASSだけを書いてPAGE_SIZEを忘れると、一覧は全件の配列のまま返り、manage.py checkも何も警告しませんでした(警告rest_framework.W001が出るのは逆の、PAGE_SIZEだけを書いた場合です)。
REST_FRAMEWORK = {
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
"PAGE_SIZE": 20,
}
PAGE_SIZEを2にして4件のデータで試すと、レスポンスは次の形に変わりました。
{"count":4,"next":"http://testserver/books/?page=2","previous":null,"results":[{"id":1,...},{"id":2,...}]}
配列からresultsを持つ辞書へ形が変わるので、既に動いているAPIに後から入れるとクライアントが壊れます。公開前に決めておく設定です。一覧の件数取得に使うCOUNT(*)の実行時間が応答時間の要件を超える場合は、CursorPaginationを検討します。関連先を含めて返す一覧では、あわせてN+1問題とは?発行クエリ数の実測と検出方法・ORM別の対策のselect_related・prefetch_relatedも入れておきます。
フィルタバックエンドによる検索・並び替えの設定
DRFには検索用と並び替え用のフィルタクラスが標準で入っており、次のようにViewSetのfilter_backendsへ並べると、クエリ文字列で検索と並び替えができます。
from rest_framework import filters
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.order_by("id")
serializer_class = BookSerializer
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ["title", "author"]
ordering_fields = ["pages"]
3件のデータで?search=yamadaは著者がyamadaの2件、?ordering=-pagesはページ数の降順を返しました。ordering_fieldsに無い?ordering=titleはエラーにならず、指定が無視されて既定の順のまま返ります。並び替えが効かないと感じたら、まずordering_fieldsを確認します。項目ごとの完全一致や範囲指定が必要になったら、django-filterのDjangoFilterBackendを使います(django-filterで一覧検索を実装する|FilterSet設計とDRF指定の判断)。
APIドキュメント:drf-spectacularによるOpenAPI生成
DRFに組み込まれたOpenAPIスキーマ生成について、公式のSchemasは「deprecated in favor of 3rd party packages」と書き、後継としてdrf-spectacularを勧めています。古い記事に出てくるCore API(coreapi)は3.17.0で削除されており、3.17.2と3.18.3のどちらでもrest_framework.schemas.coreapiはModuleNotFoundErrorになりました。
pip install drf-spectacular==0.30.0
# settings.py
INSTALLED_APPS += ["drf_spectacular"]
REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}
# config/urls.py
from django.urls import path
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns += [
path("api/schema/", SpectacularAPIView.as_view(), name="schema"),
path("api/docs/", SpectacularSwaggerView.as_view(url_name="schema")),
]
drf-spectacular 0.30.0とDRF 3.18.3の組み合わせで前章のBookViewSetから生成すると、OpenAPI 3.0.3のスキーマに/books/と/books/{id}/の2パスが入り、books_retrieve・books_partial_updateなどのoperationIdが自動で付きました。
3.17・3.18へ上げるときの破壊的変更
DRFの版番号は「3.x.0」で互換性のない変更が入りうる運用です。リリースノートの「Breaking changes」から、既存プロジェクトで影響が出やすいものを抜き出しました。
| 版 | 日付 | 変更 |
|---|---|---|
| 3.17.0 | 2026-03-18 | Python 3.9対応終了・coreapi削除 |
| 3.18.0 | 2026-08-07 | Django 4.2/5.0/5.1対応終了 |
| 3.18.0 | 2026-08-07 | many=Trueのエラーを辞書形式に |
| 3.18.1 | 2026-09-07 | 旧形式に戻す設定を追加 |
| 3.18.2 | 2026-10-06 | セキュリティ修正3件 |
many=Trueの検証エラーの辞書形式への変更
影響が大きいのは3.18.0のエラー形式です。1件目が正しく2件目が誤りの配列をBookSerializer(data=[...], many=True)で検証し、errorsを出力すると、版によって次のように違いました。
# DRF 3.17.2(入力と同じ長さの配列。正しい要素は空の辞書)
[{}, {"title": ["This field may not be blank."], "pages": ["A valid integer is required."]}]
# DRF 3.18.3(誤りのある要素だけを、インデックスの文字列をキーに持つ辞書)
{"1": {"title": ["This field may not be blank."], "pages": ["A valid integer is required."]}}
一括登録APIのエラーを配列として反復したり、errors.lengthで件数を判定したりするフロントエンドは修正が必要です。JavaScriptのerrors[i]による参照だけなら、文字列キーの辞書でも取得できます。すぐに直せない場合は、3.18.1で追加された設定で旧形式に戻せます。
REST_FRAMEWORK = {
"LIST_SERIALIZER_ERRORS_AS_DICT": False,
}
この設定で実行すると旧来の配列形式で返る一方、RemovedInDRF320Warningが出て「3.20で削除する」と告げられました。猶予は3.19までなので、移行のつなぎに留め、クライアント側を辞書形式に合わせます。こうした警告は既定では表示されないものもあるため、上げる前にpython -Wd manage.py testでテストを流し、警告を洗い出しておきます。
DRFを使うべき場面・避けるべき場面
DjangoでAPIを作る方法は、DRFのほかに素のDjango(JsonResponse)とDjango Ninjaがあります。判断の材料を並べます。
| 観点 | DRF | Django Ninja | 素のDjango |
|---|---|---|---|
| 入力の検証 | Serializer | Pydanticの型 | 手書き |
| OpenAPI | drf-spectacularを追加 | 標準で生成 | なし |
| 認証・権限 | 認証・権限クラス | 認証クラス・関数、権限は実装 | Django認証・権限の利用 |
| 非同期ビュー | 標準では非対応 | 対応 | 対応 |
| 選定の軸 | CRUD・共通ポリシー | 型定義・非同期 | 限定的なJSON応答 |
DRFを選ぶ条件
モデル単位のCRUDが複数あり、共通の権限・ページング設定を適用する管理系API、外部に公開して権限やスロットリングを細かく分けるAPI、複数人で書き方を揃えたいチーム開発ではDRFを選びます。権限・ページング・フィルタが設定1か所で全エンドポイントに効くので、APIが増えるほど差が出ます。既存のDRF資産が要件を満たしているなら継続利用を基本とし、非同期処理、型定義の共有、保守負担などの課題が移行コストを上回る場合に限って置き換えを検討します。DRFでのAPI開発や既存システムとの連携をまとめて任せたい場合は、API開発・システム連携の支援内容も参考にしてください。
DRFを入れない方がよい条件
返すJSONが数種類で、認証もDjangoのログインで足りるなら、JsonResponseで書いた方が依存が増えません。型ヒントでリクエストを定義したい、非同期ビューで外部APIを並行して呼びたい、という要件が最初からある新規開発なら、DRFよりDjango Ninjaとは?DRFとの違い・Schema・認証・非同期の実装を解説が合います。DRF 3.18.3のAPIViewにasync def getを書いて呼ぶと、コルーチンが返ってAssertionErrorになりました。公式が「feature-complete」としている以上、非同期対応が今後入る前提でDRFを選ぶのは避けます。REST APIの設計そのもの(リソースの切り方やHTTPメソッドの使い分け)から確認したい場合は、RESTとは?REST APIの仕組みと6原則・SOAP/GraphQLとの違いを実装目線で解説が前提知識になります。
よくある質問(FAQ)
DRFとDjangoの違いは何ですか?
DjangoはORM・管理画面・テンプレートまで含むWebフレームワークで、DRFはその上でWeb APIを作るための追加ライブラリです。DRFはDjango無しでは動かず、モデルや認証はDjangoのものを使います。
DRFの最新バージョンと必要な環境は?
2026年10月28日時点の最新は3.18.3(2026年10月6日公開)で、Django 5.2・6.0・6.1とPython 3.10以上に対応します。Django 4.2を使い続ける場合は3.17系が上限です。
djangorestframeworkとrest_frameworkは何が違いますか?
同じものです。djangorestframeworkはPyPIでのパッケージ名で、pip installやrequirements.txtに書きます。rest_frameworkはPythonでimportするときの名前で、INSTALLED_APPSにもこちらを書きます。
DRFのJSONレスポンスを整形して表示するには?
リクエストのAcceptヘッダをapplication/json; indent=2にすると、字下げしたJSONが返ります。ブラウザで見るだけなら、ブラウザブルAPIの画面でも整形済みで表示されます。項目名や構造を変えたいときはSerializerで調整します。
DjangoでAPIを作るならDRFとDjango Ninjaのどちらが良いですか?
複数のCRUD APIに認証・権限・ページングを共通の設定で揃えたい案件や、既存のDRF資産がある案件はDRF、型ヒントでのリクエスト定義と非同期ビューを重視する新規開発はDjango Ninjaが向きます。OpenAPIドキュメントを追加パッケージなしで出したい場合もDjango Ninjaが手軽です。