在上一篇文章中,我们探讨了为什么需要Django的类基础视图(CBV),以及它相较于函数基础视图(FBV)具有什么优势。本篇文章将详细介绍所有CBV的根本所在:Django的基础视图类

“要正确理解CBV,首先需要掌握django.views.View的运作方式和结构。”


1. Django视图类是什么?

Django的所有类基础视图都通过以下形式继承django.views.View类来使用。

from django.views import View

class MyView(View):
    def get(self, request, *args, **kwargs):
        # 处理GET请求的逻辑
        ...

    def post(self, request, *args, **kwargs):
        # 处理POST请求的逻辑
        ...

视图类的特点

  1. 按HTTP方法分离方法
    当请求到来时,Django会通过内部的dispatch()方法自动调用对应请求方法的函数,例如get()post()put()delete()

    • 相比FBV,不必使用“if request.method == 'GET': … else: …”这样的条件语句,从而使代码结构更加简洁。
  2. 继承与可扩展性
    可以通过继承视图类创建多个子视图,而将公共逻辑放在父类中,将各自逻辑放在子类中,从而最大化代码重用。


2. 视图类的基本结构和原理

Django视图结构LEGO风格

最简单的示例

# views.py
from django.http import HttpResponse
from django.views import View

class HelloCBV(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("Hello, CBV!")
  • get()方法:当浏览器发送GET请求(如在地址栏输入URL、点击链接等)时执行。

  • HttpResponse("Hello, CBV!"):直接产生一个文本格式的响应并返回。

连接urls.py

# urls.py
from django.urls import path
from .views import HelloCBV

urlpatterns = [
    path('hello/', HelloCBV.as_view(), name='hello_cbv'),
]
  • .as_view()方法将CBV转换为Django可以理解的形式。

  • 与URL模式连接后,当http://example.com/hello/接收到GET请求时,就会调用HelloCBVget()方法。


3. 视图类的用法与实际示例

像上面的示例一样,单纯“返回一个字符串”的情况并不常见。通常在实际项目中,会进行JSON响应模板渲染数据库查询等操作。这里将以JSON响应模板渲染为例进行探讨。

3.1 JSON响应处理示例

# views.py
from django.http import JsonResponse
from django.views import View

class UserDataView(View):
    def get(self, request, *args, **kwargs):
        # 假设通过GET参数接收user_id
        user_id = request.GET.get('user_id', None)
        if not user_id:
            return JsonResponse({'error': '未提供user_id'}, status=400)

        # 实际上进行数据库查询
        user_data = {
            'id': user_id,
            'name': f'User_{user_id}',
            'role': 'admin'
        }
        return JsonResponse(user_data, status=200)

    def post(self, request, *args, **kwargs):
        # 可以解析POST请求体中的JSON或者表单数据。
        # 可用request.POST、request.body、request.FILES等
        # 实际服务中,编写JSON解析后存入数据库的逻辑
        new_user_data = {
            'id': 123,
            'name': 'NewUser',
            'role': 'member'
        }
        return JsonResponse(new_user_data, status=201)
  • JsonResponse:Django中便捷生成JSON响应的工具。

  • request.GET, request.POST:可以从Django的HttpRequest对象中获取GET/POST参数。

  • 在实践中,会与数据库模型关联或使用序列器以更结构化的方式处理数据。

3.2 模板渲染示例

# views.py
from django.shortcuts import render
from django.views import View

class GreetingView(View):
    def get(self, request, *args, **kwargs):
        context = {
            'title': '欢迎来到我的网站',
            'greeting': '你好,这是一个GreetingView!'
        }
        return render(request, 'greeting.html', context)
<!-- templates/greeting.html -->
<!DOCTYPE html>
<html>
<head>
  <title>{{ title }}</title>
</head>
<body>
  <h1>{{ greeting }}</h1>
</body>
</html>
  • render()函数会找到模板文件(greeting.html),并返回渲染后的HTML作为HttpResponse

  • 这样在CBV内部也可以简单地进行模板渲染。


4. 视图类的扩展与重用性

视图类有用的一个重要原因是“代码重用”。随着项目的增大,将多个视图之间共同需要的逻辑(例如:日志记录、权限检查、公共数据处理等)放在父类中,子类继承使用会更加高效。

4.1 将公共逻辑放在父类中

# views.py
from django.http import JsonResponse
from django.views import View

class BaseLoggingView(View):
    def dispatch(self, request, *args, **kwargs):
        # 对所有请求(GET/POST等)执行公共逻辑
        print(f"[日志] {request.method} 请求 at {request.path}")
        return super().dispatch(request, *args, **kwargs)

class ChildLoggingView(BaseLoggingView):
    def get(self, request, *args, **kwargs):
        data = {'message': '来自子类的GET响应'}
        return JsonResponse(data)

    def post(self, request, *args, **kwargs):
        data = {'message': '来自子类的POST响应'}
        return JsonResponse(data)
  • dispatch()方法:是Django控制视图逻辑执行前后的核心方法。

  • 在这个示例中,处理所有请求时打印日志,然后通过super().dispatch()调用实际的get()post()等。

4.2 通过Mixin添加功能

在Django中,通常使用Mixin技术来“仅混入特定功能”。例如,将LoginRequiredMixin(检查是否登录)、PermissionRequiredMixin(检查权限)等混入到CBV中。

from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
from django.http import JsonResponse

class SecuredView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({'message': '您已通过身份验证.'})
  • Mixin的处理将在后面讲解泛型视图或权限管理时更详细地讲解,这里只需了解“这样可以组合功能”即可。

5. 与FBV的简单比较

类别 FBV(函数基础视图) CBV(类基础视图)
编写形式 以函数形式编写 以类和方法形式编写
HTTP方法处理 if request.method == 'GET': ... else: ... get()post()等按方法分开
代码重复可能性 如果逻辑复杂,容易产生重复代码 通过继承和Mixin可以重用公共逻辑
可扩展性 常常将公共逻辑混合在函数内部 类的继承结构适合应对复杂需求
可读性 对简单的原型有利 随着规模的增大,更适合保持清晰的结构
  • FBV适合快速编写小型或简单功能,而CBV则在扩展性和维护上更有优势。

  • 特别是在团队项目或中大型项目中,使用CBV会更高效。


6. 深入了解:dispatch() 与 URL 参数

Django视图请求流程图

6.1 重写dispatch()的原因

  • 公共前后处理: 每次有请求时,可以在此处理数据库连接检查、权限检查、日志记录等逻辑。

  • 方法映射: 内部使用request.method.lower()来查找并调用get()post()put()等。如果还想自定义处理诸如PATCH的方法,可以重写dispatch()进行处理。

class CustomDispatchView(View):
    def dispatch(self, request, *args, **kwargs):
        print("自定义前处理逻辑在这里.")
        response = super().dispatch(request, *args, **kwargs)
        print("自定义后处理逻辑在这里.")
        return response

6.2 接收URL参数

如果在urls.py中指定了<int:user_id>/这样的路径参数,那么我们可以在CBV的get()post()方法中通过kwargs接收这些值。

![Django视图结构LEGO风格](/media/whitedec/blog_img/view_class_lego_structure.webp)# urls.py
urlpatterns = [
    path('user/<int:user_id>/', UserDetailView.as_view(), name='user_detail'),
]

# views.py
class UserDetailView(View):
    def get(self, request, *args, **kwargs):
        user_id = kwargs.get('user_id')
        # 通过user_id进行数据库查询
        return HttpResponse(f"用户ID: {user_id}")
  • 和FBV一样,可以从kwargs中取出URL参数。

7. 更丰富的实际案例:简单的论坛示例

对于希望“更实际地使用视图类”的读者,我将给出一个简单的创建论坛列表和详情页面的示例代码。(在下一篇文章中讲解泛型视图时,可以更方便地实现CRUD,但在这里我展示了用基础视图类同样可以做到的可能性。)

# models.py - 示例模型
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
# views.py
from django.views import View
from django.shortcuts import render, get_object_or_404
from .models import Post

# 文章列表视图
class PostListView(View):
    def get(self, request, *args, **kwargs):
        posts = Post.objects.order_by('-created_at')
        context = {'posts': posts}
        return render(request, 'post_list.html', context)

# 文章详情视图
class PostDetailView(View):
    def get(self, request, *args, **kwargs):
        post_id = kwargs.get('pk')
        post = get_object_or_404(Post, pk=post_id)
        context = {'post': post}
        return render(request, 'post_detail.html', context)
<!-- post_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>帖子列表</title>
</head>
<body>
    <h1>帖子列表</h1>
    <ul>
        {% for post in posts %}
            <li>
                <a href="{% url 'post_detail' post.id %}">{{ post.title }}</a>
            </li>
        {% endfor %}
    </ul>
</body>
</html>
<!-- post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.content }}</p>
    <p>发布时间: {{ post.created_at }}</p>
</body>
</html>
# urls.py
from django.urls import path
from .views import PostListView, PostDetailView

urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
    path('posts/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]
  • PostListView:在接收GET请求时,查询所有帖子并将其传递给模板(post_list.html)。

  • PostDetailView:通过URL参数pk查找特定帖子,并将其传递给模板(post_detail.html)。

  • 这样,通过基本视图类也可以轻松构建列表和详情页面。

不过,要构建完整的CRUD,还需要Create/Update/Delete逻辑。此外,Django提供的泛型视图(Generic Views)可以大大简化这些操作,下一篇将对此进行详细讲解!


8. 结论与下一篇文章预告

到此为止,这是关于Django的基础视图类的运作方式及其在实际中的应用示例。总结核心要点如下:

  1. 继承django.views.View,重写get() / post() / dispatch()等方法,应对HTTP请求。

  2. 公共逻辑放在父类中,只需在子视图中重定义所需部分即可最大化代码重用

  3. 在模板渲染、JSON响应、数据库查询等多种场景中,轻松编写视图逻辑。

  4. 如实际示例(简单论坛)所示,仅用基础视图类也足以构建项目。
    但是,要方便处理CRUD操作,学习Django提供的泛型视图(Generic Views)会更加轻松。

下一篇文章将重点讨论通过FormView简化表单处理。通过此类,您将能学会如何自动化实现“接收用户输入、存储、失败时显示错误消息”的过程。

CBV系列将继续!

  • 第三篇:“使用FormView轻松处理表单”
  • 第四篇:“ListView & DetailView的用法”
  • 第五篇:“使用CreateView、UpdateView、DeleteView实现CRUD”
  • …(后续持续更新)

查看上一篇文章


另外参考资料

希望通过这篇文章,您对基础视图类的结构和用法有了一定了解。下一篇将讲解如何通过FormView轻松处理“表单填写→验证→错误处理→成功时重定向”。请多加关注!


“Django的视图类不仅仅是将代码按HTTP方法分开,
它还是一个能显著改善大规模项目的扩展性与维护性的强大工具。”

未来我们将继续深入探讨CBV,进行更有效率的Django开发!