大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
视图层
这一课你将能做到
- 写出一个处理 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和multipart/form-data的表单数据。JSON 提交不走这里。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"
Generic Views 自动做以下事:
- 查数据库:
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 body | request.POST 不处理 JSON | json.loads(request.body) |
CBV 里直接重写 get() 而不用框架扩展点 | 丢失分页、权限检查等 Mixin 行为 | 重写 get_queryset()、get_context_data() |
| 视图返回 Python dict | 客户端收到 500 或空响应 | 用 JsonResponse(data) |
return None 或忘记 return | Django 抛 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-CSRFTokenheader(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 交互场景。
延伸
- Django 官方文档 - 视图
- Django 官方文档 - 通用视图
- 下一课:模型与 ORM——用 Python 代码定义数据库表。