CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
视图层 FBV/CBV 与 Generic views。
视图层
这一课你将能做到
写出一个处理 GET/POST/PUT/DELETE 的函数视图(FBV),并正确返回 JSON 或 HTML
用类视图(CBV)复用 Mixin 和 Generic Views,把列表页/详情页的重复代码降到最少
理解 request 对象的生命周期和常用属性(GET、POST、headers、FILES、user)
为什么要学
视图是 Django 里「接收请求 → 处理业务 → 返回响应」的核心枢纽。Spring Boot 用 @Controller 或 @RestController 注解类来定义控制器,Django 支持函数视图(FBV)和类视图(CBV)两种形态。初学者通常先写 FBV,但生产代码里 CBV 和 Generic Views 占比更高,因为它们能消灭 80% 的重复。
人话:视图就是「用户点了什么,服务器干什么,回什么给用户」。写不好视图,路由对了也白搭。
先建立图像
请求进入
│
▼
中间件(认证、CSRF、Session)
│
▼
urls.py → 匹配到 views.py
│
├─ FBV: def article_list(request): ...
│ 手动处理 request.method
│
└─ CBV: class ArticleList(ListView):
│ 框架自动按 method 分发到 get/post
│
▼
Model / ORM(查数据库)
│
▼
Template 渲染 / JsonResponse / HttpResponse
│
▼
中间件(收尾)
│
▼
浏览器
跟着做
步骤 1:最基础的函数视图(FBV)
# blog/views.py
from django.http import JsonResponse, HttpResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def article_list(request):
if request.method == "GET":
data = ["文章1", "文章2"]
return JsonResponse({"articles": data})
elif request.method == "POST":
title = request.POST.get("title", "")
return JsonResponse({"created": title}, status=201)
require_http_methods 装饰器会拦截不支持的 HTTP 方法,直接返回 405 Method Not Allowed。
步骤 2:认识 request 对象
def inspect_request(request):
ctx = {
"method": request.method,
"path": request.path,
"GET": dict(request.GET), # 查询参数 ?page=2
"POST": dict(request.POST), # 表单数据
"headers": dict(request.headers), # HTTP 头
"is_ajax": request.headers.get("x-requested-with") == "XMLHttpRequest",
"user": str(request.user), # 已登录用户或 AnonymousUser
"session_key": request.session.session_key,
}
return JsonResponse(ctx)
关键属性:
request.GET:QueryDict,URL 参数。不是只给 GET 请求用,POST 请求里也能读 request.GET。
request.POST:只处理 application/x-www-form-urlencoded 和 的表单数据。JSON 提交不走这里。
multipart/form-data
request.body:原始字节流,适合读 JSON payload:json.loads(request.body)。
request.FILES:上传的文件字典。
request.user:当前用户实例。request.user.is_authenticated 判断是否登录。
request.META:CGI 风格的环境变量字典,如 HTTP_USER_AGENT、REMOTE_ADDR。
步骤 3:类视图(CBV)基础 from django.http import JsonResponse
from django.views import View
class ArticleView(View):
def get(self, request, *args, **kwargs):
return JsonResponse({"msg": "GET articles"})
def post(self, request, *args, **kwargs):
return JsonResponse({"msg": "POST article"}, status=201)
def put(self, request, *args, **kwargs):
return JsonResponse({"msg": "PUT article"})
def delete(self, request, *args, **kwargs):
return JsonResponse({"msg": "DELETE article"}, status=204)
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path("articles/", views.ArticleView.as_view(), name="articles"),
]
CBV 的核心是 as_view() 返回一个函数,Django 用该函数接收 request,再按 request.method 分发给 get/post/put/delete 等方法。和 Spring Boot 的 @RequestMapping + 方法分发逻辑同构。
步骤 4:Generic Views 速通列表/详情 from django.views.generic import ListView, DetailView
from .models import Article
class ArticleListView(ListView):
model = Article
template_name = "blog/article_list.html"
context_object_name = "articles"
paginate_by = 10
class ArticleDetailView(DetailView):
model = Article
template_name = "blog/article_detail.html"
context_object_name = "article"
slug_url_kwarg = "slug"
查数据库:Article.objects.all()(ListView)或 get(slug=...)(DetailView)
按主键或 slug 过滤
分页(paginate_by)
把查询结果注入模板上下文
渲染模板并返回 HttpResponse
你只需指定 model 和 template_name,就能跑通列表和详情页。
步骤 5:Mixin 复用通用逻辑 from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
class DashboardView(LoginRequiredMixin, ListView):
model = Article
template_name = "blog/dashboard.html"
login_url = "/admin/login/"
LoginRequiredMixin 会检查 request.user.is_authenticated,未登录直接重定向到 login_url。相当于 Spring Security 的 @PreAuthorize("isAuthenticated()")。
讲清楚要点
FBV 好理解,CBV 好复用 :FBV 像一个线性脚本,适合一次性逻辑。CBV 用继承和 Mixin 组合功能,适合 CRUD 这种高度重复的场景。
request 不是普通 dict :它是 HttpRequest 对象,封装了 WSGI/ASGI 环境变量、中间件注入的 session/user/csrf 等信息。不要把它当纯字典用 request["key"]。
POST 和 body 的区别 :前端用 fetch 发 JSON(Content-Type: application/json)时,数据在 request.body,不在 request.POST。request.POST 只处理传统 HTML 表单。
CBV 的 self.kwargs 和 self.args :URL 路由捕获的命名参数进 self.kwargs,位置参数进 self.args。重写 get_queryset 时常用 self.kwargs.get("pk")。
Generic Views 的扩展点 :get_queryset()、get_context_data()、get_object(),复写这些方法而不是重写 get(),能保持 Generic Views 的内建行为。
返回响应不是只能 HttpResponse :JsonResponse(自动序列化 dict)、HttpResponseRedirect(302)、HttpResponseNotFound(404)、StreamingHttpResponse(大文件流式下载)。
你可能会懵的地方
CBV 里 self.request 从哪来的?
as_view() 在创建实例后把 request 注入为实例属性。不是全局变量,是每次请求新建一个 CBV 实例。
为什么 CBV 比 FBV 更适合 Generic CRUD?
ListView/DetailView/CreateView/UpdateView/DeleteView 已经把 80% 的查库、分页、模板注入、表单处理写好了,你只填 model 和 template_name。FBV 得自己一行行写。
Spring Boot @RestController 对应 Django 的什么?
没有直接等价物。Django 最常用的是 FBV 返回 JsonResponse,或 DRF 的 @api_view(第 10 课)。Django 原生 CBV 默认渲染模板,做纯 API 需要手动返回 JSON。
视图里抛异常会怎样?
Django 会把未捕获异常交给 middleware 的 process_exception,最终返回 500 页面(DEBUG=True 时带详细堆栈)。生产环境配置 ADMINS 和日志收集。
能不能在视图里直接操作 raw SQL?
能。from django.db import connection; with connection.cursor() as cursor:。但除非 ORM 搞不定(复杂统计、CTE),否则别用 raw SQL,丧失跨数据库能力和自动转义保护。
错法对照 错法 问题 正解 request.POST.get("json_field") 读 JSON bodyrequest.POST 不处理 JSONjson.loads(request.body)CBV 里直接重写 get() 而不用框架扩展点 丢失分页、权限检查等 Mixin 行为 重写 get_queryset()、get_context_data() 视图返回 Python dict 客户端收到 500 或空响应 用 JsonResponse(data) return None 或忘记 returnDjango 抛 ValueError 所有分支必须有 return request.GET["page"] 不写默认值KeyError request.GET.get("page", "1")
卡点急救
CBV 报 TypeError: get() got an unexpected keyword argument 'pk' :URL 路由捕获了参数,但 CBV 没传 **kwargs 或 Generic View 的 pk_url_kwarg 没配置。检查 DetailView 是否把 pk_url_kwarg 设对。
POST 请求 403 Forbidden :Django 默认开启 CSRF 防护。前端发 POST 时要带上 X-CSRFToken header(Ajax)或模板里用 {% csrf_token %}(表单)。API 场景第 10 课讲怎么豁免。
request.user 是 AnonymousUser :用户没登录。视图加 @login_required 或 CBV 加 LoginRequiredMixin。检查 settings.py 的 AUTHENTICATION_BACKENDS。
面试常问
Django 的 FBV 和 CBV 怎么选?
一次性逻辑、需要大量条件分支、或和中间件紧密交互的场景用 FBV。标准化 CRUD、需要复用权限/分页/查询逻辑的场景用 CBV + Mixin。团队风格统一更重要。
类视图里 as_view() 到底做了什么?
它是一个类方法,返回一个闭包函数。每次请求进来,Django 调用这个函数,函数内部实例化 CBV 类(self = cls(**initkwargs)),然后把 request 传给 self.dispatch(),再由 dispatch 按 HTTP 方法分发到 get/post/put/delete。
Generic Views 的 get_queryset() 和 queryset 属性有什么区别?
queryset = Article.objects.all() 是静态属性,类定义时就固定。get_queryset() 是方法,每次请求都会调用,可以动态过滤(如只查当前用户的数据)。需要动态过滤时一定重写方法。
自测三问
request.POST 和 request.body 分别适用于什么 Content-Type?
为什么 CBV 更适合重复性高的 CRUD?请说出一个具体例子。
LoginRequiredMixin 和 @login_required 的区别和使用场景?
小结一句话 视图是 Django 的业务枢纽:FBV 灵活直观,CBV 和 Generic Views 靠继承消灭重复;理解 request 对象和响应类型,就能处理绝大多数 HTTP 交互场景。
延伸