Django REST Frameworkで、モデル・serializer・ViewSet・URLを接続する例です。動くURLを作ることと、誰がどの行を操作できるかは別に確認します。旧コードの名前違いを直しただけで公開しないでください。
旧画面と新しい応答の違い
元の動作確認画像を先に示します。旧Browsable APIのHTTP200・id/user_idの一覧と入力欄を示す参考画像で、正確なDjango/DRFの版や権限は画像だけから確定できません。今回の新教材はJSON専用で、同じHTML画面や旧入力欄を再現するものではありません。

新教材の方針は「登録したユーザーが自分のReportだけを作成・一覧・詳細・更新・削除できる」です。UUIDはサーバーで生成し、ownerはrequest.userから設定します。ブラウザが渡すuser_id/id/ownerを信用しません。UUIDを知りにくくすることも認可の代わりにはなりません。
| ファイル | 役割 |
|---|---|
| api/models.py | 新教材のReport定義 |
| api/serializers.py | Reportと入力/出力fieldの対応 |
| api/views.py | 認証/認可・所有者の範囲・作成時owner |
| api/urls.py | routerとurlpatterns |
| api_project/urls.py | api.urlsを/api/配下へ接続 |
次の新例は独立教材のファイル全体です。既存アプリのモデル/schemaや設定全体を置換しません。既存の認証したsessionとauth/session/CSRF middleware、対象アプリの登録・DBの準備が前提です。実アプリへ取り込むDB設計変更や配布は別に検討し、この確認のために本番migrationを実行しません。
前提
・djangoインストール済み。
・本稿ではWindows OSを例にしますが適宜読み替えて下さい。
・本稿では出来るだけAPIに関する部分のみを記述する。他に関しては別記事を参照すること。
・プロジェクト名を「api_project」とする
・適当なアプリを作成しておく。今回は「api」アプリとする。
必要ライブラリ
旧コード表の注意:これは旧導入コマンドの記録です。既存の本番環境へ無指定版を追加する指示ではありません。新教材はDjango5.2.18/DRF3.18.3で確認し、django-filterを使用しないため追加していません。importだけで検索や認可が有効になるわけではありません。
|
pip install djangorestframework pip install django-filter |
設定ファイル
旧コード表の注意:この2項目だけで既存INSTALLED_APPS全体を置き換えないでください。認証/sessionには既存のauth/contenttypes/sessions等とmiddlewareの接続が必要です。既存設定を保持して、対象アプリとrest_frameworkを適切に登録します。
| api_project/settings.py |
|---|
|
INSTALLED_APPS = ( 'api', 'rest_framework', ) |
モデルの作成
新教材は元の文字列id/user_idだけのモデルとは異なり、サーバー生成UUID、認証ユーザーへのowner、200文字以内のmessageです。ownerは応答へ出しません。
from uuid import uuid4
from django.conf import settings
from django.db import models
class Report(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
message = models.CharField(max_length=200)
旧コード表の注意:旧モデルはidとuser_idの2列を定義しており「idのみ」という説明は誤りです。このuser_idは単なる文字列で、認証したユーザーとの関係を保証しません。新教材のUUID/owner/messageは別のモデル定義で、既存DBの列をそのまま変更する指示ではありません。
| api/models.py |
|---|
|
from django.db import models
id = models.CharField(max_length=200, primary_key=True) user_id = models.CharField(max_length=200) |
※DB設定はAPI固有のものではないので省きます。
旧定義にはidとuser_idがあります。新しい教材では下記の別モデルを使い、ownerを認証ユーザーから設定します。
シリアライザーの作成
api/serializers.pyです。Meta.modelをReportに合わせ、応答はid/messageのみ。作成/PUTのmessageは空でない文字列を必須とし、余計なid/owner/user_id等の入力は拒否します。PATCHは部分更新です。
from rest_framework import serializers
from .models import Report
class ReportSerializer(serializers.ModelSerializer):
class Meta:
model = Report
fields = ("id", "message")
read_only_fields = ("id",)
def to_internal_value(self, data):
if not isinstance(data, dict) or set(data) - {"message"}:
raise serializers.ValidationError({"detail": "Only message may be submitted."})
if "message" in data and type(data["message"]) is not str:
raise serializers.ValidationError({"message": "Expected a string."})
return super().to_internal_value(data)
旧コード表の注意:この旧表のラベルはapi/models.pyですが、serializerは新例のapi/serializers.pyに分離します。定義したReportと、importのUser/Entry・Meta.model=Userが一致していません。旧fieldsはid/user_idの明示指定であり、全field指定ではありません。名前をReportへ直すだけでも入力user_idを認証/認可として信用してはいけません。
| api/models.py |
|---|
|
from rest_framework import serializers from .models import User, Entry
|
※DB設定はAPI固有のものではないので省きます。
URLの設定
まずプロジェクトのapi_project/urls.py、次にアプリのapi/urls.pyを示します。前者は教材上project_urls.pyというファイル名で保存しています。既存プロジェクトでは他のルートを保持し、include対象と実ファイル名を照合します。
from django.urls import include, path
# Logical location: api_project/urls.py. Preserve other existing project routes.
urlpatterns = [path("api/", include("api.urls"))]
from rest_framework.routers import SimpleRouter
from .views import ReportViewSet
router = SimpleRouter()
# No class-level queryset: supply basename explicitly.
router.register("test", ReportViewSet, basename="report")
urlpatterns = router.urls
旧コード表の注意:標準的なプロジェクト側ファイル名はurls.pyです。includeの文字列api.urlsと実ファイル/モジュール名を合わせ、既存adminや他のURLをこの短い一覧で消さないでください。URLの接続だけで認証や許可は設定されません。
| api_project/url.py |
|---|
|
from django.contrib import admin from django.urls import include, path
urlpatterns = [ path('admin/', admin.site.urls), path('api/', include('api.urls')), ] |
旧コード表の注意:この旧表はapi/url.pyと書かれていますが、include先はapi.urlsです。routerを作るだけでなくurlpatterns=router.urlsを公開する必要があります。新例はクラスのquerysetを使わないためbasenameを明示します。
| api/url.py |
|---|
|
from rest_framework import routers from .views import TestView
router = routers.DefaultRouter() router.register(r'test', TestView) |
GET /api/test/を解決するには、プロジェクトのinclude、アプリのurlpatterns、ViewSetとserializer/モデルが正しく接続している必要があります。旧コードはそのままでは不一致があり、アクセス成功を断定できません。
ビューの作成
api/views.pyです。IsAuthenticatedだけで他人の行を保護せず、全操作のget_querysetをownerで限定し、作成時はperform_createでownerを確定します。管理者が他人の行を操作する例外・共有・業務のモデル権限は実装していません。
from io import BytesIO
from rest_framework.authentication import SessionAuthentication
from rest_framework.exceptions import APIException, UnsupportedMediaType
from rest_framework.pagination import PageNumberPagination
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated
from rest_framework.renderers import JSONRenderer
from rest_framework.viewsets import ModelViewSet
from .models import Report
from .serializers import ReportSerializer
class BodyTooLarge(APIException):
status_code = 413
default_detail = "JSON body too large."
class BoundedJSONParser(JSONParser):
def parse(self, stream, media_type=None, parser_context=None):
encoding = (parser_context or {}).get("encoding") or "utf-8"
if encoding.lower().replace("-", "") != "utf8":
raise UnsupportedMediaType(media_type)
raw = stream.read(4097)
if len(raw) > 4096:
raise BodyTooLarge()
return super().parse(BytesIO(raw), media_type, parser_context)
class ReportPagination(PageNumberPagination):
page_size = 20
page_size_query_param = "page_size"
max_page_size = 50
class ReportViewSet(ModelViewSet):
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,)
parser_classes = (BoundedJSONParser,)
renderer_classes = (JSONRenderer,)
serializer_class = ReportSerializer
pagination_class = ReportPagination
def get_queryset(self):
return Report.objects.filter(owner=self.request.user).order_by("id")
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
旧コード表の注意:この旧ビュー表のファイル名もapi/url.pyですが、新例ではapi/views.pyです。serializerという単数のimport名も実ファイルと照合します。全Reportのquerysetに認可指定がないまま、モデル/import/URLだけを直して公開しません。router登録はアプリのurls.pyへまとめます。
| api/url.py |
|---|
|
import django_filters from .models import Report class TestView(viewsets.ModelViewSet): router = routers.DefaultRouter() |
動作の確認
開発サーバーを起動して「http://127.0.0.1:8000/api/test/」にアクセスする。
旧画面は序盤の「旧画面と新しい応答の違い」に保持しています。HTTP200や一覧が表示されることだけで、匿名拒否・他人の操作拒否・入力検証の成功とは判断しません。
上の旧コード/画面は保守資料です。新教材の確認範囲と結果は末尾に分けて示します。
新しいURLと結果の確認
GET/POSTは/api/test/、詳細GET/PUT/PATCH/DELETEは/api/test/{UUID}/です。新しいViewSetにclass属性querysetがないため、router.registerでbasename="report"を明示し、report-list/report-detailの逆引きも検査しています。一覧は一意なid順の既定20/最大50件で、UUID順は作成時刻順ではありません。同時更新中のsnapshotも保証しません。
新例では匿名は403、認証した所有者は自分の一覧/詳細を読めます。他人や存在しないUUIDは404にし、既知の他人のUUIDでも変更/削除できません。新規作成は201でid/message、削除は204で本文なしです。空の一覧もページング用のcount/next/previous/resultsを持つため、旧画像の配列だけの応答とは形が違います。
| 結果 | 確認点 |
|---|---|
| 403 | このsession方式の匿名/CSRF拒否。CSRF成功だけで認可成功ではない。 |
| 400 | JSON構文/最上位object、messageの型/必須/長さ、許可外field。 |
| 404 | URL未接続・UUID形式・対象不在・所有者の範囲を分ける。 |
| 405/406 | methodとURL、Accept。新例はJSONRendererのみなので旧HTMLフォームは表示しない。 |
| 413/415 | 解析する本文4096byteの上限、application/json/UTF-8。 |
SessionAuthenticationは認証済みsessionのunsafe methodでCSRFを検査します。トークン準備/送信はAjax POSTの教材を参照し、403の対処にCSRFを外したりCORS/許可Originを広げたりしません。このparserは使用するJSON本文の制限であり、未使用のDELETE本文等も含めたWeb層の容量/連打制限や監査を代行しません。通信失敗時に自動でPOSTを再送する機能もありません。
壊れた接続と認可不足を混同しない
旧表ではReportだけを定義しながらUser/Entryをimportし、Meta.modelもUserでした。またapi.urlsをincludeする一方でurl.pyという表記、アプリ側のurlpatterns不足、views/serializerのファイル名も食い違っていました。したがって「掲載どおり動く匿名CRUD」とは扱いません。
独立検体では未定義import、urlpatternsのないURLモジュール、querysetなしでbasenameを省略した登録がそれぞれ失敗することを確認しました。別の合成対照では、接続を成立させて全件queryset/既定AllowAnyとすると匿名操作/所有者の偽装が可能なことも確認しています。これは修正前の全コードが実行できた証明でも、CWiki本体にDRFの脆弱性があるという判定でもありません。応答の型はモデルJSON化と公開fieldも参照してください。
検証範囲と公式資料
2026年10月26日確認。Python3.12.10/Django5.2.18/DRF3.18.3/SQLite3.49.1の新しいメモリDBで13試験が成功しました。新例の5ファイルを実際にimportしてrouter/resolve/reverseを照合し、実User/DB sessionとCSRF検査で匿名/所有者/別人CRUD、UUID生成/入力偽装、拒否時Report不変、JSONの型/文字数/encoding/容量、一覧上限、404/405/406を検査しました。
本番ログイン/POST、DB schema/依存/設定の変更やmigrationは実行していません。実ブラウザ/Browsable API、TLS/proxy/CORS、Token/JWT、MySQL/PostgreSQL、並列更新/負荷、全業務認可・不正対策は未検証です。実データを削除するAPIとしてこの教材のDELETEを試さず、独立した合成データだけで確認します。
公式資料:router・basename・include、ViewSetとget_queryset、permissionの既定値、session認証とCSRF。
子ページはありません
- 環境構築とプロジェクト/アプリの作成
- MVC(MVT)のそれぞれの使い方と説明
- データベースへの接続と操作
- Django Administration
- git管理
- エラー一覧
- バージョンの確認方法
- ログ出力方法
- SQLのログ出力方法
- ログのローテート設定
- settings.pyの定数にアクセスする方法
- 本番環境へのインストールとアプリのデプロイ(apache編)
- 本番環境へのインストールとアプリのデプロイ(nginx編)
- djangoアプリの本番の開始URLを変更する
- 静的(static)ファイルの置き場所と読み込み(画像、css、js )
- CSRFトークンをAjaxで使用する方法
- ajaxの使用例(POST編)
- ファイルのアップロードとファイルの名前
- クイックスタート/チュートリアル
- ログイン機能
- テンプレート側のログイン判定
- ビュー側のログイン判定
- 管理者ユーザーの作成/判定と管理画面
- モデルのjson化とレスポンス
- runserverでポートを指定する方法
- cronによるバッチ実行
- テンプレートで利用する共通のcontextを定義する方法
- プログラムが本番サーバーで反映されない場合の対処法
- APIの作成
- cron用コマンド・ファイルの作成
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスのソフト一覧|Blender・Metasequoiaと用途別の選び方
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Visual Studio「デザイナーを表示できません」エラーの原因と対処 2026-10-28 12:06:00
- SSH 認証に失敗しました。再試行してください の対処 2026-10-28 12:06:00
- SSH2 秘密鍵の読み込みに失敗しました の原因と対処 2026-10-28 12:06:00
- TeraTerm のよくあるエラーと対処法完全ガイド — Connection refused / 2026-10-28 12:06:00
- Visual Studio エラー一覧|コンパイルエラー・警告の確認方法と対処 2026-10-28 11:39:57
- Whitelabel Error Page の原因と消し方|Spring Boot のエラーページ 2026-10-28 11:39:57
- Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法 2026-10-28 11:39:57
- SQL WHERE 句完全ガイド 2026-10-28 11:39:57
- SQL JOIN 完全ガイド (INNER/LEFT/RIGHT/FULL OUTER) 2026-10-28 11:39:57
- tomcat の起動 / 停止ログと catalina.log・catalina.out の違い 2026-10-28 10:24:23
- Tomcat の起動確認方法 5 つ|localhost:8080・ポート・プロセス・ログ 2026-10-28 10:24:23
- Oracle 19c COMMITの使い方: 暗黙コミット・自動確定・応答不明時の確認 2026-10-28 09:59:09
- Oracle 19c DELETE文|削除対象の確認・COMMIT前の判断・ROLLBACKの条件 2026-10-28 09:59:09
- Oracle 19c INSERT文|INSERT ALL・SELECT・RETURNINGとMERGEの使い分け 2026-10-28 09:59:09
- Oracle 19cのトランザクション制御|COMMIT・ROLLBACK・分離レベルと接続設定 2026-10-28 09:59:09