← 返回题目列表

Django 函数视图和类视图有什么区别?CBV 的优缺点是什么?

高频 中等 第 3 / 27 题 更新于 2026/07/27
DjangoFBVCBV类视图

简化版

函数视图 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"

主要区别:

对比点FBVCBV
写法函数
优点简单直观复用强、可继承、可组合 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 的视图体系。

九、常见误区与追问

对比点FBVCBV
组织形式函数直接处理请求类、方法和 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。