将mixins与基于类的视图一起使用


本文整理自网络,侵删。

在基于类的视图中使用mixins

警告: 这是一个高级主题。在探索这些技术之前,建议您对Django基于类的视图有一定的了解。

Django的内置基于类的视图提供了许多功能,但您可能需要单独使用其中的一些功能。例如,您可能想编写一个视图,该视图呈现一个模板以进行HTTP响应,但是您不能使用 TemplateView; 也许您只需要在上渲染模板POST,然后GET完全执行其他操作即可。虽然您可以TemplateResponse直接使用 ,但这可能会导致代码重复。

因此,Django还提供了许多混合器,这些混合器提供了更多离散功能。例如,模板呈现封装在中 TemplateResponseMixin。Django参考文档包含所有mixins的完整文档。

上下文和模板答复

提供了两个中央mixin,它们有助于在使用基于类的视图中的模板时提供一致的界面。

  • TemplateResponseMixin每个返回a的内置视图都 TemplateResponse将调用提供的 render_to_response() 方法TemplateResponseMixin。在大多数情况下,都会为您调用此get()方法(例如,由TemplateView和 实现的方法都调用它DetailView);同样,不太可能需要覆盖它,尽管如果您希望响应返回的内容不是通过Django模板呈现的,那么您就需要这样做。有关此示例,请参见JSONResponseMixin示例。render_to_response()本身调用 get_template_names(),默认情况下会 template_name在基于类的视图上查询;其他两个mixin(SingleObjectTemplateResponseMixin 和 MultipleObjectTemplateResponseMixin)覆盖此属性,以便在处理实际对象时提供更灵活的默认值。
  • ContextMixin每个需要上下文数据(例如用于渲染模板(包括TemplateResponseMixin上述内容))的内置视图都应调用 get_context_data()传递它们想要确保位于其中的任何数据作为关键字参数。 get_context_data()返回字典;在ContextMixin其中返回其关键字参数,但是通常会覆盖此参数以将更多成员添加到字典中。您也可以使用该 extra_context属性。

建立Django基于类的通用视图

让我们看看Django的两个基于类的通用视图是如何通过提供离散功能的mixin构建的。我们将考虑 DetailView,它呈现一个对象的“详细”视图,而 ListView,它将呈现一个对象列表(通常来自查询集),并可选地对它们进行分页。这将向我们介绍四个mixin,它们在使用单个Django对象或多个对象时提供有用的功能。

也有参与通用编辑观点混入(FormView和特定型号的意见CreateView, UpdateView和 DeleteView),并在基于日期的通用视图。这些已在mixin参考文档中介绍。

DetailView:使用单个Django对象

要显示对象的详细信息,我们基本上需要做两件事:我们需要查找对象,然后需要TemplateResponse使用合适的模板制作一个 ,并将该对象作为上下文。

要获得该对象,需要DetailView 依赖SingleObjectMixin,该get_object() 方法提供了一种方法,该 方法可以根据请求的URL找出对象(它会根据URLConf中的声明查找pk和slug关键字参数,然后从model视图的属性中查找该对象 ,或提供的 queryset 属性)。SingleObjectMixin也会覆盖 get_context_data(),它在所有Django的所有基于类的内置视图中使用,以为模板渲染提供上下文数据。

然后TemplateResponse,要 DetailView使用SingleObjectTemplateResponseMixin,则使用 , 如上所讨论的,它扩展了TemplateResponseMixin,覆盖 get_template_names()。实际上,它提供了一组相当复杂的选项,但是大多数人要使用的主要选项是 /_detail.html。该_detail部分可以通过设置来改变 template_name_suffix 的一个子类别的东西。(例如,通用编辑观点使用_form的创建和更新的意见,并 _confirm_delete进行删除的意见。)

ListView:使用许多Django对象

对象列表遵循大致相同的模式:我们需要一个(可能是分页的)对象列表,通常为 QuerySet,然后我们需要TemplateResponse使用该对象列表使用合适的模板制作一个 。

要获取对象,请ListView使用 MultipleObjectMixin,同时提供 get_queryset() 和 paginate_queryset()。与with不同SingleObjectMixin,不需要关闭URL的一部分来确定要使用的查询集,因此默认值使用 view类上的querysetor model属性。覆盖get_queryset() 此处的常见原因 是动态地改变对象,例如取决于当前用户或排除博客的将来帖子。

MultipleObjectMixin还重写 get_context_data()以包括用于分页的适当上下文变量(如果禁用了分页,则提供虚拟变量)。它依赖object_list于作为关键字参数进行传递的关键字参数ListView。

做一个TemplateResponse, ListView然后使用 MultipleObjectTemplateResponseMixin; 与SingleObjectTemplateResponseMixin 上面的方法一样,此方法将覆盖,get_template_names()以提供,最常用的是 ,而该部分再次从 属性中获取。(基于日期的通用视图使用诸如的后缀, 以此类推,以便为各种专门的基于日期的列表视图使用不同的模板。)a range of options/_list.html``_listtemplate_name_suffix_archive``_archive_year

使用Django的基于类的视图混入

现在,我们已经了解了Django的基于类的通用视图如何使用提供的mixins,让我们看一下将它们组合在一起的其他方法。当然,我们仍将它们与内置的基于类的视图或其他通用的基于类的视图结合起来,但是您可以解决许多比Django开箱即用的罕见问题。

警告:并非所有的mixin都可以一起使用,也不是所有基于通用类的视图都可以与所有其他mixin一起使用。在这里,我们提供了一些可行的示例。如果要合并其他功能,则必须考虑使用的不同类之间重叠的属性和方法之间的交互,以及方法解析顺序将如何影响以何种顺序调用哪些版本的方法。

Django的基于类的视图和基于类的视图mixin的参考文档将帮助您了解哪些属性和方法可能会导致不同的类和mixin之间发生冲突。

如有疑问,通常最好还是放弃并以 View或为基础TemplateView,也许使用 SingleObjectMixin和 MultipleObjectMixin。尽管您可能最终会写出更多的代码,但是以后其他人可能更容易理解它,而更少的交互性让您担心,可以节省一些时间。(当然,您总是可以深入了解Django基于类的通用视图的实现,以获取有关如何解决问题的灵感。)

SingleObjectMixin与View一起使用

如果我们要编写一个仅对做出响应的基于类的视图,则将创建子POST类View并post()在该子类中编写一个方法。但是,如果我们希望我们的处理能够处理从URL识别的特定对象,我们将需要提供的功能 SingleObjectMixin。

我们将通过在基于通用类的视图简介中Author使用的模型来 演示这一点。

的views.py 

from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.urls import reverse
from django.views import View
from django.views.generic.detail import SingleObjectMixin
from books.models import Author
?
class RecordInterest(SingleObjectMixin, View):
  """Records the current user's interest in an author."""
  model = Author
?
  def post(self, request, *args, **kwargs):
      if not request.user.is_authenticated:
          return HttpResponseForbidden()
?
      # Look up the author we're interested in.
      self.object = self.get_object()
      # Actually record interest somehow here!
?
      return HttpResponseRedirect(reverse('author-detail', kwargs={'pk': self.object.pk}))

实际上,您可能希望将兴趣记录在键值存储而不是关系数据库中,因此我们省略了这一点。唯一需要担心使用的视图 SingleObjectMixin就是我们要查找感兴趣的作者的地方,它通过调用来完成 self.get_object()。mixin会为我们处理其他所有事务。

我们可以很容易地将其挂接到我们的URL中:

的urls.py 

from django.urls import path
from books.views import RecordInterest
?
urlpatterns = [
  #...
  path('author/<int:pk>/interest/', RecordInterest.as_view(), name='author-interest'),
]

请注意pk命名组,该组 get_object()用于查找Author实例。您还可以使用slug或的任何其他功能 SingleObjectMixin。

SingleObjectMixin与  使用ListView

ListView提供内置的分页功能,但是您可能希望对所有通过外键链接到另一个对象的对象列表进行分页。在我们的出版示例中,您可能希望对特定出版商的所有书籍进行分页。

一种实现方法是与结合ListView使用 SingleObjectMixin,以便分页图书清单的查询集可以脱离作为单个对象找到的出版商。为此,我们需要有两个不同的查询集:

  • Book 由...使用的查询集 ListView由于我们可以访问Publisher要列出的书,因此我们可以覆盖get_queryset()和使用Publisher的反向外键管理器。
  • Publisher queryset用于 get_object()我们将依靠的默认实现get_object()来获取正确的Publisher对象。但是,我们需要显式传递一个queryset参数,因为否则默认的的实现get_object()将调用 get_queryset()我们已重写以返回Book对象而不是对象的对象Publisher。

注意:我们必须仔细考虑get_context_data()。由于SingleObjectMixin和 ListView都会将事物放置在上下文数据中(context_object_name如果已设置)的值之下,因此我们将明确确保事物在 Publisher上下文数据中。ListView 将增加在合适的page_obj和paginator我们提供我们记得打电话super()。

现在我们可以写一个新的PublisherDetail:

from django.views.generic import ListView
from django.views.generic.detail import SingleObjectMixin
from books.models import Publisher
?
class PublisherDetail(SingleObjectMixin, ListView):
  paginate_by = 2
  template_name = "books/publisher_detail.html"
?
  def get(self, request, *args, **kwargs):
      self.object = self.get_object(queryset=Publisher.objects.all())
      return super().get(request, *args, **kwargs)
?
  def get_context_data(self, **kwargs):
      context = super().get_context_data(**kwargs)
      context['publisher'] = self.object
      return context
?
  def get_queryset(self):
      return self.object.book_set.all()

请注意我们是如何设置的self.object,get()以便稍后在get_context_data()和中再次使用它get_queryset()。如果您未设置template_name,则模板将默认为正常 ListView选择,在这种情况下,这是 "books/book_list.html"因为它是一本书籍清单; ListView一无所知SingleObjectMixin,因此毫无 头绪Publisher。

该paginate_by所以你不必创建大量的书籍,看到分页的工作是在故意例如小!这是您要使用的模板:

{% extends "base.html" %}
?
{% block content %}
  <h2>Publisher {{ publisher.name }}</h2>
?
  <ol>
    {% for book in page_obj %}
      <li>{{ book.title }}</li>
    {% endfor %}
  </ol>
?
  <div class="pagination">
      <span class="step-links">
          {% if page_obj.has_previous %}
              <a >previous</a>
          {% endif %}
?
          <span class="current">
              Page {{ page_obj.number }} of {{ paginator.num_pages }}.
          </span>
?
          {% if page_obj.has_next %}
              <a >next</a>
          {% endif %}
      </span>
  </div>
{% endblock %}

避免任何更复杂

一般来说,你可以使用 TemplateResponseMixin和 SingleObjectMixin当你需要它们的功能。如上所示,稍加注意,您甚至可以SingleObjectMixin与结合使用 ListView。但是,随着您尝试这样做,事情变得越来越复杂,一个好的经验法则是:

阅读剩余部分

相关阅读 >>

使用wsgi进行部署

错误报告

Django 安装

基于类的视图简介

Django 简介

部署清单

Django 的安全性

Django 教程介绍

Django 使用表单模板

内置基于类的通用视图

更多相关阅读请进入《Django》频道 >>




打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...