以下文章是 Django 类基视图(CBV)探索系列的 第七篇,讨论如何利用 Mixin重用通用功能,并有效实现 权限管理登录检查。在上一篇文章(第六篇)中,我们已通过 TemplateViewRedirectView 实现了简单的页面渲染和重定向的自动化,现在将了解如何最大化 CBV 的 重用性扩展性

上一篇的直接链接请点击以下链接!

TemplateView & RedirectView 的利用方法


“通过 Django Mixin 提高代码的重用性,  

简洁地实现权限和登录检查!”


1. Mixin,为什么需要?

Mixin 概念图 - 组装功能块的类结构

Django CBV 本身提供了强大的重用性,但如果多个视图中存在 普遍需要的功能,该如何处理呢?例如,访问某个页面常常需要 登录,或者只有具有 特定权限 的用户才能访问。这时就引入了 Mixin 的概念。

Mixin 是利用 多重继承 的概念,将 相同的方法或属性注入 到多个类中。在 Django CBV 中,通过将实现了特定功能的 Mixin 类继承给视图类,可以减少 重复代码,并提高 可维护性

主要 Mixin 的使用示例:

  1. 权限管理:检查访问特定视图的用户是否已登录,或是否具有特定权限。
  2. 表单处理逻辑:在多个表单中通用的数据预处理或后处理逻辑。
  3. 模板上下文:为所有视图添加需要共同传递的上下文数据。
  4. 日志记录:在特定视图的执行前后添加日志记录功能。

2. Django 的代表性 Mixin 使用法:LoginRequiredMixin

最常用的 Mixin 之一就是 LoginRequiredMixin。顾名思义,它的作用是强制要求用户在访问该视图时 必须登录

2.1 LoginRequiredMixin 的使用方法

LoginRequiredMixin 包含在 django.contrib.auth.mixins 模块中。

# views.py
from django.views.generic import ListView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Post # 示例模型

class MyPostListView(LoginRequiredMixin, ListView):
    model = Post
    template_name = 'posts/my_posts.html'
    context_object_name = 'posts'

    # 未登录用户访问时重定向的 URL 设置(可选)
    # login_url = '/accounts/login/' # settings.LOGIN_URL 为默认值
    # redirect_field_name = 'next'  # 登录后返回原页面时使用的查询参数名(默认为 next)
  • 继承顺序:首先继承 LoginRequiredMixin,然后继承 Django 的通用视图(这里是 ListView)。Mixin 主要用于添加功能,因此通常将其放在核心视图类之前。
  • LoginRequiredMixin 会自动将未登录用户重定向到指定的 settings.LOGIN_URL 登录页面。登录成功后会通过 next 查询参数返回到原请求的页面。

2.2 urls.py 的连接

应用了 LoginRequiredMixin 的视图与普通 CBV 连接在 urls.py 中是相同的。

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

urlpatterns = [
    path('my-posts/', MyPostListView.as_view(), name='my_posts'),
]

现在访问 /my-posts/ 路径时,如果未登录,则会自动转到登录页面。


3. 权限管理的 Mixin:PermissionRequiredMixin & UserPassesTestMixin

如果 LoginRequiredMixin 是强制要求用户登录,那么 PermissionRequiredMixinUserPassesTestMixin 则允许基于 特定权限特定条件 来访问。

登录 & PermissionMixin 概念图 - 访问权限检查

3.1 PermissionRequiredMixin 的使用方法

基于 Django 的权限系统,只有具有特定 permission 的用户才能访问。

# views.py
from django.views.generic import CreateView
from django.contrib.auth.mixins import PermissionRequiredMixin
from .models import Article
from .forms import ArticleForm

class ArticleCreateView(PermissionRequiredMixin, CreateView):
    model = Article
    form_class = ArticleForm
    template_name = 'articles/article_form.html'
    success_url = '/articles/'

    # 通过 'app_label.permission_codename' 格式指定权限
    permission_required = 'app_label.add_article'
    # 若任一权限存在则允许访问:
    # permission_required = ('app_label.add_article', 'app_label.change_article')
    # raise_exception = True # 无权限时抛出 403 Forbidden 错误(默认重定向到登录页面)
  • permission_required:以字符串(一个或元组)形式指定访问所需的权限。通常使用 Django auth 应用自动生成的权限(add_model, change_model, delete_model, view_model)。

3.2 UserPassesTestMixin 的使用方法

当希望检查更 复杂或自定义的条件 时使用。例如,可以实现只能修改自己写的文章,或只允许特定组用户访问等逻辑。

# views.py
from django.views.generic import UpdateView
from django.contrib.auth.mixins import UserPassesTestMixin
from .models import Comment
from .forms import CommentForm

class CommentUpdateView(UserPassesTestMixin, UpdateView):
    model = Comment
    form_class = CommentForm
    template_name = 'comments/comment_form.html'
    success_url = '/comments/'

    # 重写 test_func 方法以检查条件
    def test_func(self):
        comment = self.get_object()
        # 检查当前登录用户是否是评论的作者
        return self.request.user == comment.author or self.request.user.is_superuser

    # raise_exception = True # 测试失败时抛出 403 Forbidden 错误
  • test_func() 方法如果返回 True 则允许访问,如果返回 False 则拒绝访问。
  • self.request.user 可以访问当前登录的用户对象。

4. 自定义 Mixin 的创建

除了 Django 提供的 Mixin 外,还可以创建 自定义 Mixin 在多个视图中重用。例如,创建一个添加所有页面通用的 current_year 上下文的 Mixin。

# common/mixins.py
from datetime import datetime

class CurrentYearMixin:
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['current_year'] = datetime.now().year
        return context

# views.py (应用示例)
from django.views.generic import TemplateView
from .common.mixins import CurrentYearMixin

class MyCustomPageView(CurrentYearMixin, TemplateView):
    template_name = 'my_custom_page.html'

# my_custom_page.html (模板中使用)
<footer>&copy; {{ current_year }} 我的官方网站</footer>
  • CurrentYearMixin 通过重写 get_context_data 方法来添加 current_year 到上下文中。调用 super().get_context_data(**kwargs) 是为了保持上层类(或其他 Mixin)的上下文数据非常重要。

5. 与 FBV 对比

功能 FBV(基于函数的视图) CBV + Mixin
登录/权限检查 使用 @login_required, @permission_required 装饰器。 <br> 自定义条件通常在函数内手动编写 if not request.user.is_authenticated: return redirect(...) 等。 通过继承 LoginRequiredMixin, PermissionRequiredMixin, UserPassesTestMixin 等 Mixin 轻松实现。 <br> 类似于装饰器,但更与 CBV 结构整合。
代码重用性 通过调用单独的函数或手动编写函数装饰器来隔离通用逻辑。 <br> 容易产生重复代码。 通过 Mixin 模块化所需的功能并通过多重继承注入。 <br> 结构化程度更高且重用性更强。
代码可读性 每个函数中条件检查逻辑可能重复。 仅需查看视图类的继承列表,即可理解应用了哪些功能(权限、登录等)。
可维护性 添加新功能时可能需要修改多个函数。 仅需修改 Mixin 类,所有使用该 Mixin 的视图会自动生效。
开发生产力(核心关键词) “每个函数的独立处理,重复工作的可能性” “模块化,功能扩展容易,遵循 DRY(不要重复自己)原则,提高生产力”

6. 结论及下篇预告

Mixin 是 Django 类基视图的 真正力量 的关键要素。通过它,我们可以最大化 代码的重用性,模块化 通用功能,以简化视图类的复杂性,尤其是像 认证和权限管理 这样的 Web 应用必要元素能够非常高效地实现。

现在我们已经探讨了从 CBV 的基本用法到复杂的 CRUD,以及通过 Mixin 扩展功能,几乎涵盖了 Django CBV 的所有核心。

在下一篇(系列第八篇)中,我们将讨论如何将之前讨论的 CBV 实际应用于项目,以及在 复杂场景中 如何组合使用,并综合总结 CBV 的优点和局限性,结束本系列。


查看上一篇内容

  1. 类基视图(CBV)探索系列 #1 – 从 FBV 到 CBV 的原因以及开发者的态度
  2. 类基视图(CBV)探索系列 #2 – 了解 Django 的基本视图类
  3. 类基视图(CBV)探索系列 #3 – 使用 FormView 简化表单处理
  4. 类基视图(CBV)探索系列 #4 – ListView & DetailView 的利用方法
  5. 类基视图(CBV)探索系列 #5 – 使用 CreateView、UpdateView、DeleteView 实现 CRUD
  6. 类基视图(CBV)探索系列 #6 – TemplateView & RedirectView 的利用方法

“利用 Mixin 发挥 Django CBV 的潜力,  

构建易于维护和扩展的 Web 应用!”