Djangoのビューは、リクエストから必要な処理を行い応答を返す入口です。ファイル分割だけでなく、入力と操作権限をどこで確認するかを分けて読みます。
Viewに関する項目を子ページから選択してください。
| この記事の要点 |
|
View とは
Django MVT (Model-View-Template) パターンの「View」は、Web リクエストを受け取り、必要な処理を実行してレスポンスを返す Python 関数 / クラスです。他のMVCでControllerと呼ばれる処理に似る面もありますが、名称だけの1対1対応ではありません。
views.pyは慣習的な配置です。URLconfからimportできる呼出し先であれば別モジュールやviews/パッケージにも分けられます。DjangoのMVTと他のMVCを名前だけで1対1対応とせず、URL選択・middleware・ビュー・テンプレートを区別します。
目的に合わせて子ページを選ぶ
Viewに関する項目を子ページから選択してください。このページは役割と境界の入口です。具体的な配置や用途は次の既存ページに整理しています。
| 調べたいこと | 子ページ |
|---|---|
| URLからビューへつなぐ | URLディスパッチャー(ルーティング処理) |
| GET/POSTの入力を受け取る | GETとPOSTパラメータ受け取り |
| 関数をファイルへ分ける | 関数ベースビューの作り方とviewの分割 |
| クラスをファイルへ分ける | クラスベースビューの作り方とviewの分割 |
| CBVでHTTPメソッドを扱う | クラスベースビューでGET/POSTリクエストの受け取り方 |
| cookieを扱う | cookieの値の設定と取得 |
| レスポンスのステータスを選ぶ | HTTPステータスコードの返し方 |
| 別のページへ送る | ビューでリダイレクト |
| テンプレートへデータを渡す | クラスベースビューでテンプレートに値を渡す方法 |
Function-based View (FBV) — 最も基本
旧コードの注意:次はモデル・テンプレート等を省略した旧断片です。createはrequest.POSTをそのまま保存し、欠落キーで例外にもなります。認証・操作権限・入力検証・対象範囲が含まれないため、そのまま公開しません。具体的なフォームと所有者の例をこの記事で分けて示します。
# myapp/views.py
from django.http import HttpResponse
from django.shortcuts import render, redirect, get_object_or_404
from .models import Article
# 最小の View
def hello(request):
return HttpResponse("Hello, Django!")
# テンプレートをレンダー
def index(request):
articles = Article.objects.all()
return render(request, "myapp/index.html", {"articles": articles})
# 詳細ページ
def detail(request, article_id):
article = get_object_or_404(Article, pk=article_id)
return render(request, "myapp/detail.html", {"article": article})
# POST 処理 + リダイレクト
def create(request):
if request.method == "POST":
Article.objects.create(
title=request.POST["title"],
body=request.POST["body"],
)
return redirect("myapp:index")
return render(request, "myapp/create.html")
追加教材:フォームで検証するFBV
次はhub_demo/fbv.pyです。フォームとモデルは「追加教材のモデルとフォーム」、URLとテンプレートは「追加教材のURLとテンプレート」に掲載します。既存myappの旧断片へ連結しないでください。
from django.contrib.auth.decorators import login_required, permission_required
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
from .forms import ArticleForm
@login_required
@permission_required("hub_demo.add_article", raise_exception=True)
@require_http_methods(["GET", "POST"])
def create_article(request):
form = ArticleForm(request.POST if request.method == "POST" else None)
if request.method == "POST" and form.is_valid():
article = form.save(commit=False)
article.owner = request.user
article.save()
return redirect("hub_demo:index")
return render(request, "hub_demo/article_form.html", {"form": form})
ログイン→add_article権限→GET/POST許可を確認し、有効なPOSTだけ保存します。空入力のGETは未バインド、空のPOSTはバインド済みの無効フォームで、request.POST or Noneのように空POSTをGET扱いしません。ownerは送信値でなくrequest.userを使います。POST後のredirectはブラウザの通常再送を避ける流れで、二重送信・同時更新の完全防止ではありません。
URL マッピング
旧コードの注意:次は旧myapp向けのURL断片です。対応するモデル/テンプレートが必要で、ここへのURL登録だけで認証や権限は追加されません。追加教材のhub_demoとは別の例です。既存URLconfを全体ごと置き換えません。
# myapp/urls.py
from django.urls import path
from . import views
app_name = "myapp"
urlpatterns = [
path("", views.index, name="index"),
path("hello/", views.hello, name="hello"),
path("article/<int:article_id>/", views.detail, name="detail"),
path("create/", views.create, name="create"),
]
# プロジェクト全体の urls.py
# project/urls.py
from django.urls import include, path
urlpatterns = [
path("myapp/", include("myapp.urls")),
]
追加教材のモデルとフォーム
hub_demoは独立した検証用アプリです。本人の記事だけを扱う方針で、Articleにはowner/title/body/created_atを定義します。ownerやIDをフォームのfieldsへ含めません。実運用のモデルや権限管理を丸ごと置き換える例ではありません。
hub_demo/models.py
from django.conf import settings
from django.db import models
class Article(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
title = models.CharField(max_length=200)
body = models.TextField(max_length=4000)
created_at = models.DateTimeField(auto_now_add=True)
hub_demo/forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ["title", "body"]
ModelFormは空/文字数等を検証しますが、fieldsに無いownerへの操作権限までは決めません。下記のビュー例でログイン・モデルの操作権限・ownerの取得範囲を別々に確認します。DBへ直接書く別経路や別APIまでこのチェックで守られるわけではありません。
Class-based View (CBV)
クラスベースは再利用性が高く、ミックスインで認証・権限・ページネーションを足せます。
旧コードの注意:次のHelloView.postは保存を実装せずリダイレクトするだけです。302が返っても保存成功の証拠にはなりません。クラスをURLへ渡す場合はas_view()で呼出し先を作り、GET/POST等のメソッドと認証/CSRFを別に確認します。
# myapp/views.py
from django.views import View
from django.shortcuts import render, redirect
from .models import Article
class HelloView(View):
def get(self, request):
return render(request, "myapp/hello.html")
def post(self, request):
# POST 処理
return redirect("myapp:index")
# urls.py 側
# path("hello/", views.HelloView.as_view(), name="hello"),
ジェネリック CBV — 共通の処理を再利用
CRUD で頻出のパターンを Django が用意してくれています:
| ジェネリック View | 用途 | テンプレート規約 |
|---|---|---|
ListView | 一覧 | app/model_list.html |
DetailView | 詳細 | app/model_detail.html |
CreateView | 新規作成 | app/model_form.html |
UpdateView | 編集 | app/model_form.html |
DeleteView | 削除 | app/model_confirm_delete.html |
TemplateView | テンプレート表示。contextで動的な値も渡せる | 任意 |
RedirectView | リダイレクト | テンプレート不要 |
旧コードの注意:次の旧CRUD例は認証・モデル権限・所有者範囲・フォーム/テンプレートを省略しています。DeleteViewは削除するビューで、URL公開だけでもアクセス制限にはなりません。orderingのcreated_atだけでは同時刻のページ順が確定しません。追加教材は操作権限とownerの絞込み、-pkの同点順を示します。
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.urls import reverse_lazy
from .models import Article
class ArticleListView(ListView):
model = Article
paginate_by = 20 # ページネーション
ordering = ["-created_at"] # 降順
class ArticleDetailView(DetailView):
model = Article
class ArticleCreateView(CreateView):
model = Article
fields = ["title", "body"]
success_url = reverse_lazy("myapp:index")
class ArticleUpdateView(UpdateView):
model = Article
fields = ["title", "body"]
success_url = reverse_lazy("myapp:index")
class ArticleDeleteView(DeleteView):
model = Article
success_url = reverse_lazy("myapp:index")
追加教材:操作権限と対象範囲を分けるCBV
hub_demo/views.py
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .forms import ArticleForm
from .models import Article
class OwnerScopeMixin:
def get_queryset(self):
return super().get_queryset().filter(owner=self.request.user)
class ArticleListView(LoginRequiredMixin, OwnerScopeMixin, ListView):
model = Article
paginate_by = 20
ordering = ["-created_at", "-pk"]
class ArticleDetailView(LoginRequiredMixin, OwnerScopeMixin, DetailView):
model = Article
class ArticleCreateView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
model = Article
form_class = ArticleForm
permission_required = "hub_demo.add_article"
success_url = reverse_lazy("hub_demo:index")
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class ArticleUpdateView(LoginRequiredMixin, PermissionRequiredMixin, OwnerScopeMixin, UpdateView):
model = Article
form_class = ArticleForm
permission_required = "hub_demo.change_article"
success_url = reverse_lazy("hub_demo:index")
class ArticleDeleteView(LoginRequiredMixin, PermissionRequiredMixin, OwnerScopeMixin, DeleteView):
model = Article
permission_required = "hub_demo.delete_article"
http_method_names = ["get", "post", "head", "options"]
success_url = reverse_lazy("hub_demo:index")
LoginRequiredMixinを先頭に置き、PermissionRequiredMixinでadd/change/deleteのモデル権限を確認します。通常のModelBackendでモデル権限を持つだけでは「他人の記事を操作しない」という条件にならないため、一覧/詳細/更新/削除のget_querysetをownerで絞ります。対象外のIDは404、ログイン済みで操作権限が無ければ403になります。未ログインは既存のログイン画面へ送る前提で、この教材にログイン画面やユーザー発行処理は含めません。既定権限の用意/付与も別途必要です。
DeleteViewのGETは確認画面で、実際の削除は有効なPOSTです。この例ではDELETEメソッドも明示的に許可していません。サンプルの削除は物理削除で、ゴミ箱・監査・復旧・論理削除・排他制御や二重処理防止は実装していません。既存DBの行へ試さず、保持や削除承認が必要な業務へそのまま採用しません。各リクエストの許可は別経路も含めて設計します。
レスポンスの種類
get_object_or_404は成功時にモデルを返すhelperで、render/JsonResponse等のHTTP応答とは別です。GETのクエリやformの値を任意のredirect先にせず、固定の名前付きURLを使います。相対/外部URLを扱う場合は送り先の許可条件が必要です。
| 関数 / クラス | 用途 |
|---|---|
HttpResponse(text) | 素のレスポンス |
render(request, template, context) | HTML テンプレートをレンダー |
redirect(name_or_url) | リダイレクト。既定302で、指定/対象版によって異なる |
JsonResponse({...}) | JSON 返却 (Content-Type 自動) |
HttpResponseNotFound | 404 |
HttpResponseForbidden | 403 |
get_object_or_404(Model, pk=id) | 取得成功ならモデル、未取得ならHttp404を送出するhelper |
FileResponse(open(...)) | ファイルダウンロード |
デコレータで認証 / メソッド制限
旧コードの注意:次のpass関数は応答を返さない省略断片で、呼び出すと適切なHttpResponseを返せません。csrf_exemptはimportだけではCSRFを解除しませんが、解決策としてデコレーターを足しません。LoginRequiredMixin等の順序も重要で、モデル権限だけでは個別データへの権限は保証されません。
from django.contrib.auth.decorators import login_required, permission_required
from django.views.decorators.http import require_http_methods, require_POST, require_GET
from django.views.decorators.csrf import csrf_exempt
@login_required
def my_page(request):
return render(request, "myapp/mypage.html")
@require_POST
def post_only(request):
# GET でアクセスすると 405 Method Not Allowed
pass
@require_http_methods(["GET", "POST"])
@login_required
def secure_view(request):
pass
@permission_required("myapp.delete_article")
def admin_only(request):
pass
# CBV の場合
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
class SecureView(LoginRequiredMixin, View):
login_url = "/login/"
def get(self, request):
return render(request, "myapp/secure.html")
追加教材のURLとテンプレート
既存のauth/contenttypesとセッション・AuthenticationMiddleware・CsrfViewMiddleware・テンプレート探索を維持し、hub_demoを登録します。これは設定一式や本番配備の手順ではありません。hub_demo/__init__.pyは空にし、ROOT_URLCONFをhub_demo.project_urlsとした独立検体で確認しています。既存のURLconfやアプリ一覧を上書きせず、ログイン先も既存の安全な画面に合わせます。
hub_demo/urls.py
from django.urls import path
from .fbv import create_article
from .views import (ArticleListView, ArticleDetailView, ArticleCreateView,
ArticleUpdateView, ArticleDeleteView)
app_name = "hub_demo"
urlpatterns = [
path("", ArticleListView.as_view(), name="index"),
path("new/", ArticleCreateView.as_view(), name="create"),
path("new-function/", create_article, name="create-fbv"),
path("<int:pk>/", ArticleDetailView.as_view(), name="detail"),
path("<int:pk>/edit/", ArticleUpdateView.as_view(), name="update"),
path("<int:pk>/delete/", ArticleDeleteView.as_view(), name="delete"),
]
hub_demo/project_urls.py
from django.urls import include, path
urlpatterns = [path("articles/", include("hub_demo.urls"))]
hub_demo/templates/hub_demo/article_form.html
<!doctype html>
<html lang="ja"><meta charset="utf-8"><title>Article Form</title>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Save</button>
</form>
</html>
hub_demo/templates/hub_demo/article_list.html
<!doctype html>
<html lang="ja"><meta charset="utf-8"><title>My Articles</title>
<ul>{% for article in object_list %}<li>{{ article.title }}</li>{% empty %}<li>Empty</li>{% endfor %}</ul>
{% if page_obj.has_previous %}<a href="?page={{ page_obj.previous_page_number }}">Previous</a>{% endif %}
{% if page_obj.has_next %}<a href="?page={{ page_obj.next_page_number }}">Next</a>{% endif %}
</html>
hub_demo/templates/hub_demo/article_detail.html
<!doctype html>
<html lang="ja"><meta charset="utf-8"><title>Article</title>
<h1>{{ object.title }}</h1>
<p>{{ object.body }}</p>
</html>
hub_demo/templates/hub_demo/article_confirm_delete.html
<!doctype html>
<html lang="ja"><meta charset="utf-8"><title>Confirm Deletion</title>
<p>Delete {{ object.title }}?</p>
<form method="post">{% csrf_token %}<button type="submit">Delete</button></form>
</html>
formと削除確認はcsrf_tokenを含め、表示値は通常のHTMLエスケープに任せます。safe/autoescape offでユーザー文字列をHTML化しません。CSRFと認証、モデル権限、対象行の範囲は別の確認です。request.userの利用には認証middlewareが必要で、ヘッダーやフォームからユーザーを決めません。
FBV vs CBV — どちらを選ぶ?
以下は構造の違いを見る観点で、人気調査や性能実測の順位ではありません。FBVも関数/デコレーターで共通化でき、CBVが自動で安全・高速になるわけではありません。
| 観点 | FBV | CBV |
|---|---|---|
| 学習コスト | 関数・デコレーター・分岐を追う | dispatch・継承・MROも追う |
| コード量 | 要件と共通化次第 | 汎用処理と要件が合う場合は短くできる |
| 柔軟性 | 条件分岐を明示しやすい | 拡張hookと継承関係を確認する |
| 再利用 | 関数やデコレーターでも共通化できる | mixin等で共通化できる |
| おすすめ | 独自処理、API エンドポイント | 典型的な CRUD |
FAQ
Q: View で DB を直接叩いて良い?
A: 単純な処理なら OK。複雑なビジネスロジックは services.py 等に切り出して View は薄く保つのが定石。
Q: API エンドポイントは何を使う?
A: Django REST Framework の APIView / ViewSet を使うとシリアライザ・パーミッション・ページネーションが揃って便利。
Q: 404 と 500 を独自テンプレートにするには?
A: Djangoの標準エラービューを使う場合、探索できる場所に404.html/500.htmlを用意し、DEBUG=Falseの隔離環境で実際の404/500応答を確認します。プロジェクト側templatesならTEMPLATESのDIRSも照合します。独自handlerや外側Webサーバーが応答する構成、詳細を表示するDEBUG=Trueとは分け、Host設定だけでテンプレート探索が完了するとは扱いません。本番で故意に例外を起こしません。
根拠と検証範囲(2026年10月26日)
Django公式:ビュー、汎用編集ビュー、認証/モデル権限とmixin、ModelFormの許可項目、render/redirect/取得helper、MVC/MVTの呼称を確認しました。
追加教材はPython3.12.10/Django5.2.18で、新しいSQLite検体と仮ユーザー/権限だけを作って検査しています。匿名/権限不足/他人のID、CSRF、空/長すぎる入力、owner偽装、GET確認/POST更新、HTMLエスケープ、同時刻のページ順を確認。既存DBへの接続・migration・本番設定/認証変更は行っていません。ログイン画面や実パスワード認証・並行更新/本番DB/ASGI/全middleware・保持/監査の要件は未検証であり、この教材だけで本番CRUDが完成するという意味ではありません。
人気ページ
- 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