65.9K
CodeProject 正在变化。 阅读更多。
Home

类视图 Django 教程

2018年1月10日

CPOL

9分钟阅读

viewsIcon

6087

Django 的宗旨是提高程序员的生产力。作为一个 Web 应用程序框架,它提供了工具和结构,帮助 Web 开发人员快速完成工作。

本文首发于 Tests4Geeks

Django 的宗旨是提高程序员的生产力。作为一个 Web 应用程序框架,它提供了工具和结构,帮助 Web 开发人员快速完成工作。

开发人员如今使用应用程序框架的原因在于,绝大多数 Web 编程是极其重复的:处理传入请求、检查会话信息、认证用户、CRUD 数据库操作等等。

使 Web 应用程序独一无二的代码比例非常小。框架帮助开发人员满足优秀代码设计的关键原则之一:不要重复自己(保持 DRY)。

视图函数和类视图的需求

Django 项目提供的第一个工具之一是视图函数

def list_user_snippets(request):
    # Show list of a user's code snippets
    # for a Pastebin-like application
    snippets = Snippet.objects.filter(user=request.user)
    context = {'snippets':snippets}
    return render(request, 'mysnippets/snippet_list.html', context)

如果您正在使用 Django 的视图函数,上面四行代码为您做了很多工作。函数传入的参数request是 Django 创建的一个对象,它接收服务器提供的数据,符合 WSGI 规范,并将这些数据转换成易于使用的 Python 对象。

通过 request 对象,您可以轻松检查 GET 参数、POST 数据,查看用户是否已登录等。

调用 Snippet.objects.filter() 使用 Django 的对象关系映射器查询用户代码片段的数据库,而 render 函数将提供的 context 字典与模板绑定,并将模板作为 HttpResponse 对象返回给 Django 框架,Django 利用该对象创建符合 WSGI 规范的响应发送给 Web 服务器。

在 Django 中使用视图函数可以节省大量开发时间,使开发人员能够腾出精力解决新问题,而不是一遍又一遍地解决同样的老问题。

但是,当您开始构建应用程序时,您会很快注意到大量重复的代码。

例如,如果我们现在希望我们的应用程序不仅能显示用户的代码片段,还能包含一个列出所有现有片段的 URL 怎么办?

def list_all_snippets(request):
    # Show list of all code snippets
    # for a Pastebin-like application - looking pretty repetitive...
    snippets = Snippet.objects.all()
    context = {'snippets':snippets}
    return render(request, 'mysnippets/snippet_list.html', context)

我们创建一个新的视图,它的工作方式与 list_user_snippets() 视图基本相同,但现在我们查询的是所有代码片段,而不是特定用户的代码片段。

代码仍然很容易编写,但如果您痴迷于干净的代码,您可能会担心事情的发展方向。我们的应用程序很可能会需要更多具有极其相似代码的视图。

一个更大的问题是,我们可能会开始开发更复杂的视图,然后发现我们的代码库中到处都是复杂的视图,它们之间只有微小的差异。

让我们再来看一个我们应用程序的视图。我们可以按用户列出代码片段,也可以列出所有代码片段。现在我们想列出网站上的所有评论

def list_all_comments(request):
    # Show list of all comments - hmmm, looks familiar
    comments = Comment.objects.all()
    context = {'comments':comments}
    return render(request, 'mysnippets/comment_list.html', context)

list_all_snippets()list_all_comments() 这样的视图函数急需一个抽象层。这两个视图完全相同,唯一的区别是将“snippet”一词替换为“comment”一词。Django 为帮助程序员编写更少重复的代码做了很多工作,但如果您只是复制粘贴视图函数来更改每个视图中的一个单词,那么很明显,还需要做更多工作来保持代码的非重复性(即“DRY”)。

快速入门类通用视图

Django 核心开发人员意识到许多视图函数基本上在做同样的工作,只是细微差别不同。最初,Django 提供了基于函数的通用视图,这使得为常见用例创建视图更加容易,但在 Django 1.3 中,团队引入了基于类的通用视图,它为创建视图提供了更强大的抽象。

通过继承和 mixin,基于类的通用视图提供了一系列功能,只需几行代码即可访问。

这是原始的 list_all_snippets() 视图,重写为继承 Django 内置 ListView 类的基于类的视图

class SnippetListAll(ListView):
      model = Snippet

就是这样!ListView 类负责查询数据库和渲染模板。我们只需要指定视图应使用哪个模型。由于我们的 SnippetListAll 类非常简单,只重写了其父类的一个属性,实际上我们可以跳过编写视图,而只需在 urls.py 中指定要重写的属性。

# in urls.py

urlpatterns = [
    url(r'all-snippets', views.ListView.as_view(model=Snippet)),
]

在许多情况下,根本不需要编写视图,只需向内置通用视图的 as_view() 方法提供一些参数即可完成您需要的所有工作。

使用源码

在进一步深入 Django 的通用视图之前,有一个重要问题需要解决:基于类的视图的两个示例代码量很少,因此开始理解幕后发生的事情至关重要。

随着框架承担越来越多的工作,代码变得看似不那么直观,要弄清楚如何使用您继承的通用视图可能会令人沮丧。关键在于永远不要将框架视为一个黑箱。

与其猜测事物的工作原理,不如不怕深入研究源代码,这样在 Django 中编程会更容易。

这是 Django 1.9 版本中 ListView 的代码

# https://git.io/vaAhu
# github.com/django - django/views/generic/list

class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
    """
    Render some list of objects, set by `self.model` or `self.queryset`.
    `self.queryset` can actually be any iterable of items, not just a queryset.
    """

ListView 通过继承 MultipleObjectTemplateResponseMixinBaseListView 来提供其功能。这些类的代码非常易读。BaseListView 不到 20 行,它定义了一个方法。以下是摘录

# https://git.io/vaAhQ
# github.com/django - django/views/generic/list

class BaseListView(MultipleObjectMixin, View):
    """
    A base view for displaying a list of objects.
    """
    def get(self, request, *args, **kwargs):
        self.object_list = self.get_queryset()
        allow_empty = self.get_allow_empty()

        # ...

        context = self.get_context_data()
        return self.render_to_response(context)

我们可以沿着内置通用视图的继承树,发现所有提供给我们的方法和属性,它们的作用以及工作方式。只需花几分钟阅读源代码,就可以节省数小时的沮丧,并帮助我们开始回答诸如……

等等,模板在哪里?

在上一个部分中,两个通用视图示例都没有提及模板,但我们知道我们要渲染一个模板,那么发生了什么?

快速查看 ListViews 的 mixin MultipleObjectTemplateResponseMixin 可以回答我们的问题

# https://git.io/vaxJo
# github.com/django - django/views/generic/list

class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
    template_name_suffix = '_list'

    def get_template_names(self):
        # ...code removed here
        if hasattr(self.object_list, 'model'):
            opts = self.object_list.model._meta
            names.append("%s/%s%s.html" % (opts.app_label, opts.model_name, self.template_name_suffix))

        return names

我们可以看到,MultipleObjectTemplateResponseMixin 导致 ListView 自动检查名为 appname/modelname_list.html 的模板。

您可以在 Github 上或在 Python 安装的 site-packages 目录中本地阅读所有 Django 通用视图的源代码。

阅读代码不仅消除了 Django 为我们所做工作的神秘感,还有助于我们更精细地控制如何使用内置的基于类的通用视图。

对视图进行更多控制

我们已经看到了如何通过重写 ListView 上的 model 属性来定制通用视图。这是最基本的示例,但还有一些更常见的用例值得我们关注。

如果我们不想使用 appname/modelname_list.html 约定来命名我们的模板怎么办?也许我们有一个名为“show_snippets.html”的模板,它位于 TEMPLATES 设置中明确指定的 my_templates 目录中。

只需在我们的视图中重写 template_name 属性即可

更改模板名称

class SnippetListAll(ListView):
      model = Snippet
      template_name = "my_templates/show_snippets.html"

我们应用程序提供的另一个视图是“按成员显示片段”视图。要使用 ListView 提供此功能,我们需要通过重写 get_queryset() 方法来控制 queryset 属性的值。

自定义查询

class SnippetListByUser(ListView):
      template_name = "my_templates/show_snippets.html"
      def get_queryset(self):
          return Snippet.objects.filter(user=self.request.user)

可以直接设置 queryset,但在这种情况下,我们使用 get_queryset(),以便查询可以访问 self.request.user 对象。

有一个属性在几乎所有情况下都应该被重写,那就是 context_object_name。默认情况下,ListView 使用名为 object_list 的对象渲染模板。

这意味着设计师最终会编写一个看起来像这样的模板

{% for comment in object_list %}
   {{ comment }}
{% endfor %}

而写成这样会更自然

{% for comment in comments %}
   {{ comment }}
{% endfor %}

为了在创建模板时更轻松,最好在视图中指定 context_object_name

自定义上下文对象名称

class CommentList(ListView):
    model = Comment
    context_object_name = 'comments'

更高级的用例与我在此处说明的基本用例非常相似。例如,您可能希望使用 ListView 功能,但还需要添加一些额外的数据供模板渲染。在这种情况下,请重写 get_context_data() 方法

如果您不熟悉 Python 的面向对象编程,了解您可以使用 super() 调用父对象以获得默认行为会很有帮助。因此,您可以返回父级方法的实现,但使用您在方法中提供的参数进行调用,也可以先调用父级实现并获取其结果,然后在您的方法中修改结果。

Django 通用视图的源代码包含了许多关于如何使用 super() 的示例。

更多通用视图

ListView 是处理通用视图的一个很好的起点。有一些更简单的视图可能很有用,例如 TemplateView,也有一些更复杂的视图可以为您处理大量工作,例如 CreateViewUpdateView

了解 Django 所有可用的基于类的通用视图不需要太长时间,并且可以节省编写样板代码的时间。

以下是对您可能最常使用的部分视图的简要概述

  • TemplateView – 渲染指定的模板
  • RedirectView – 重定向到指定的 URL
  • DetailView – 显示对象的详细信息
  • ListView – 显示对象的集合
  • CreateView – 渲染一个表单来创建对象,提供验证并更新数据库
  • UpdateView – 渲染一个表单来编辑对象,提供验证并更新数据库
  • DeleteViewGET 方法显示确认屏幕,POST 方法从数据库删除对象

Django 还包含处理基于日期的功能的基于类的通用视图。像 ArchiveIndexViewDateDetailView 这样的视图使得提供诸如按日期列出博客文章和按发布日期查看条目等功能更加容易。

更多资源

如果您认真对待 Django 开发,您必须学会使用基于类的视图和内置的通用视图。除了查看 Tests4Geeks 博客获取详细教程和深入指南外,还有一些其他资源您可能想了解。

最好的资源是 Django 源代码本身——并且与 官方 Django 文档 互为补充。

浏览构成 Django 的基于类的通用视图的 mixin 的一个优秀资源是 Django 类视图检查器

最后,一旦您掌握了内置的通用视图,就值得看看一个提供类似功能的更简单的库,Django Vanilla Views

Tom 来自 MDashX,他开发 Web 应用和自动化解决方案,自 1.0 版本以来一直在使用 Django。

© . All rights reserved.