Django 函数视图和类视图有什么区别?CBV 的优缺点是什么?
简化版
函数视图 FBV 是用函数处理请求,简单直接;类视图 CBV 是用类组织请求处理逻辑,可以通过继承、Mixin 和通用视图复用代码。简单接口用 FBV 很清爽,CRUD、列表详情、权限组合较多时 CBV 更方便,但 CBV 继承链复杂时可读性会下降。
详细版
FBV 示例:
def article_detail(request, pk):
article = get_object_or_404(Article, pk=pk)
return render(request, "detail.html", {"article": article})
CBV 示例:
from django.views.generic import DetailView
class ArticleDetailView(DetailView):
model = Article
template_name = "detail.html"
主要区别:
| 对比点 | FBV | CBV |
|---|---|---|
| 写法 | 函数 | 类 |
| 优点 | 简单直观 | 复用强、可继承、可组合 Mixin |
| 适合 | 简单逻辑、少量分支 | CRUD、通用列表、详情、表单 |
| 风险 | 重复代码 | 继承链复杂、调试门槛高 |
面试时可以说:CBV 不是替代 FBV 的“高级写法”,而是针对可复用场景的组织方式。
完整版教学
一、FBV 为什么受欢迎
函数视图非常直观:
def article_list(request):
articles = Article.objects.filter(status="published")
return render(request, "list.html", {"articles": articles})
请求进来调用函数,函数返回响应。没有继承链,没有隐藏方法,适合:
- 简单页面;
- 小型接口;
- 特殊业务流程;
- 逻辑步骤很明确的视图。
很多团队即使熟悉 CBV,也会保留大量 FBV,因为可读性确实好。
二、CBV 的核心是 dispatch
类视图最终也要变成可调用对象。URLconf 中常见写法:
path("articles/", ArticleListView.as_view(), name="article_list")
as_view() 会返回一个可调用对象。请求进来后,CBV 通常通过 dispatch() 根据 HTTP 方法分发到 get()、post() 等方法:
from django.views import View
class HelloView(View):
def get(self, request):
...
def post(self, request):
...
这让同一个资源的 GET/POST 逻辑可以组织在一个类里。
三、通用类视图减少重复代码
Django 提供很多通用类视图:
ListView:列表页;DetailView:详情页;CreateView:创建表单;UpdateView:更新表单;DeleteView:删除确认;TemplateView:静态模板页。
例如列表页:
class ArticleListView(ListView):
model = Article
template_name = "article_list.html"
context_object_name = "articles"
paginate_by = 20
def get_queryset(self):
return Article.objects.filter(status="published")
这比每个列表页都手写分页、模板上下文、对象查询更容易复用。
四、Mixin 是 CBV 的重要能力
CBV 常通过 Mixin 组合能力:
class ArticleEditView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = Article
fields = ["title", "content"]
permission_required = "blog.change_article"
Mixin 可以把登录校验、权限校验、表单处理、上下文扩展拆成独立组件。
但 Mixin 也带来问题:多个父类方法解析顺序依赖 MRO,如果不熟悉继承链,调试会变难。
五、如何选择 FBV 和 CBV
实战中可以这样选:
- 逻辑简单、流程特殊:FBV;
- 标准 CRUD:CBV;
- 需要复用权限、分页、表单流程:CBV + Mixin;
- 团队新人多、继承链复杂:FBV 可能更稳;
- API 项目:也可以选择 Django REST Framework 的 APIView/ViewSet,而不是 Django 原生 CBV。
不要为了显得高级强行用 CBV。代码可读性和维护成本才是关键。
六、常见误区
- 认为 CBV 性能更高。二者性能差异通常不是核心,组织方式才是重点。
- 滥用多层继承。继承链太深会让代码像迷宫。
- 不理解
as_view(),在 URLconf 里直接写类名。 - 在通用类视图里硬塞非常特殊的业务流程,导致覆盖一堆钩子方法,反而不如 FBV 清楚。
七、CBV 的核心不是“类”,而是可组合的处理流程
类视图的优势不在于比函数视图高级,而在于把请求处理拆成可复用方法。比如 ListView 把获取查询集、分页、模板上下文、响应渲染拆成多个钩子;你可以重写 get_queryset() 控制数据来源,重写 get_context_data() 补充模板变量,而不必复制整段列表页逻辑。
这也是 CBV 容易难懂的原因:它依赖继承、Mixin 和方法解析顺序。使用通用类视图时,要知道自己重写的是哪个钩子,以及父类在哪个阶段调用它。否则很容易出现“代码能跑,但不知道为什么”的情况。
面试时可以用 as_view() 解释类视图如何变成可调用对象。URLconf 里注册的是 MyView.as_view(),它会返回一个函数;请求进来后实例化视图对象,再根据 HTTP 方法分发到 get()、post() 等方法。这样类视图最终仍然符合 Django “视图是可调用对象并返回响应”的规则。
八、什么时候用 FBV,什么时候用 CBV
函数视图适合逻辑简单、流程直观的接口。例如一个健康检查接口、一个简单回调、一个只有几行逻辑的页面,用 FBV 更清楚。类视图适合多个页面共享模式,例如列表、详情、创建、编辑、删除,或者需要组合认证、权限、分页、过滤等通用能力。
但不要为了“面向对象”强行使用 CBV。过深继承和过多 Mixin 会让代码阅读成本上升。比较好的原则是:如果 CBV 能显著减少重复、让钩子语义更清楚,就用;如果逻辑很特殊、读函数更直接,就用 FBV。
在 DRF 中,APIView、GenericAPIView、ViewSet 也是 CBV 思路的延伸。它们通过类和 Mixin 组织认证、权限、序列化、查询集、分页等能力。理解 Django CBV,有助于理解 DRF 的视图体系。
九、常见误区与追问
| 对比点 | FBV | CBV |
|---|---|---|
| 组织形式 | 函数直接处理请求 | 类、方法和 Mixin 组织流程 |
| 优势场景 | 简单、特殊、直观逻辑 | 标准 CRUD、复用钩子、组合能力 |
| 主要风险 | 重复代码变多 | 继承链和 MRO 难读 |
- 误区:CBV 一定比 FBV 高级。 CBV 只是组织方式不同,适合复用流程;简单逻辑用 FBV 往往更清楚。
- 误区:CBV 性能天然更好。 面试重点通常不是性能差异,而是
as_view()、dispatch()、Mixin 和通用视图的复用机制。 - 误区:URLconf 可以直接写类名。 通常要写
MyView.as_view(),因为 Django 需要的是可调用视图对象。 - 追问:dispatch 做什么? 它根据 HTTP 方法把请求分发给
get()、post()等处理方法,并承担类视图流程入口。 - 追问:Mixin 为什么容易出问题? 多重继承依赖 MRO,方法名和调用顺序不清楚时会出现覆盖、漏调父类方法等问题。
- 追问:DRF 的 APIView/ViewSet 和 CBV 有什么关系? 它们延续 CBV 思路,把认证、权限、序列化、查询集和动作分发组织成可复用类体系。
记忆钩子:FBV 胜在直,CBV 胜在复用;CBV 的灵魂不是“类”,而是
as_view -> dispatch -> get/post这条处理链。
十、加强记忆
FBV 是函数,胜在直观;CBV 是类,胜在复用。CBV 通过 as_view() 变成可调用视图,通过 dispatch() 分发 HTTP 方法,通过通用视图和 Mixin 减少重复代码。简单逻辑用 FBV,标准 CRUD 和可复用流程用 CBV。