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


当前第2页 返回上一页

暗示:您的每个视图都应仅使用mixins或一组基于类的通用视图中的视图:详细信息,列表,编辑和日期。例如,将TemplateView(内置于视图中)与 MultipleObjectMixin(通用列表)结合在一起是很好的选择 ,但是将SingleObjectMixin(通用细节)与MultipleObjectMixin(通用列表)结合起来可能会遇到问题。

为了显示当您尝试变得更复杂时会发生什么,我们展示了一个示例,该示例在存在更简单的解决方案时会牺牲可读性和可维护性。首先,让我们看一下与结合DetailView使用的天真尝试 , FormMixin以使我们能够 POST将Django Form带到与我们使用来显示对象相同的URL DetailView。

FormMixin与   使用DetailView

回想一下我们先前使用View和 SingleObjectMixin在一起的示例。我们正在记录用户对特定作者的兴趣;现在说,我们要让他们留下信息,说明他们为什么喜欢他们。再次,让我们假设我们不会将其存储在关系数据库中,而是存储在我们不再担心的更深奥的东西中。

此时,很自然地可以Form将封装从用户浏览器发送到Django的信息。还要说我们在REST上投入了巨资,因此我们想使用与显示来自用户的消息相同的URL来显示作者。让我们重写我们的代码AuthorDetailView。

尽管必须将a添加到上下文数据中,以便将其呈现在模板中,但我们将保留GET来自的处理。我们还希望从中引入表单处理,并编写一些代码,以便在表单上正确调用。DetailViewFormFormMixinPOST

注意:我们使用FormMixin并实现 post()自己,而不是尝试DetailView与之 混合FormView(这post()已经提供了合适的方法),因为这两个视图都实现get(),并且事情会变得更加混乱。

我们的新AuthorDetail外观如下所示:

# CAUTION: you almost certainly do not want to do this.
# It is provided as part of a discussion of problems you can
# run into when combining different generic class-based view
# functionality that is not designed to be used together.
?
from django import forms
from django.http import HttpResponseForbidden
from django.urls import reverse
from django.views.generic import DetailView
from django.views.generic.edit import FormMixin
from books.models import Author
?
class AuthorInterestForm(forms.Form):
  message = forms.CharField()
?
class AuthorDetail(FormMixin, DetailView):
  model = Author
  form_class = AuthorInterestForm
?
  def get_success_url(self):
      return reverse('author-detail', kwargs={'pk': self.object.pk})
?
  def post(self, request, *args, **kwargs):
      if not request.user.is_authenticated:
          return HttpResponseForbidden()
      self.object = self.get_object()
      form = self.get_form()
      if form.is_valid():
          return self.form_valid(form)
      else:
          return self.form_invalid(form)
?
  def form_valid(self, form):
      # Here, we would record the user's interest using the message
      # passed in form.cleaned_data['message']
      return super().form_valid(form)

get_success_url()提供重定向到的位置,该位置可用于的默认实现form_valid()。post()如前所述,我们必须提供我们自己的。

更好的解决方案

之间微妙的相互作用的数量 FormMixin和DetailView已测试我们的管理事物的能力。您不太可能想自己编写此类。

在这种情况下,尽管编写处理代码涉及很多重复,但是您可以post()自己编写方法,并保持 DetailView唯一的通用功能 Form。

可替代地,它仍然会比上面的方法工作少到具有用于处理的形式,其可以使用一个单独的视图 FormView从不同 DetailView无顾虑。

另一种更好的解决方案

我们实际上在这里试图做的是使用来自同一URL的两个不同的基于类的视图。那么为什么不这样做呢?我们在这里有一个非常清楚的划分:GET请求应获取 DetailView(Form添加到上下文数据中),POST请求应获取FormView。让我们先设置这些视图。

该AuthorDisplay视图与我们首次引入AuthorDetail时几乎相同;我们在写我们自己get_context_data(),使 AuthorInterestForm可用的模板。get_object()为了清楚起见,我们将跳过之前的 替代:

from django import forms
from django.views.generic import DetailView
from books.models import Author
?
class AuthorInterestForm(forms.Form):
  message = forms.CharField()
?
class AuthorDisplay(DetailView):
  model = Author
?
  def get_context_data(self, **kwargs):
      context = super().get_context_data(**kwargs)
      context['form'] = AuthorInterestForm()
      return context

然后AuthorInterest是a FormView,但是我们必须带进来, SingleObjectMixin以便我们可以找到我们正在谈论的作者,并且我们必须记住进行设置template_name以确保表单错误将呈现与AuthorDisplayon 相同的模板GET:

from django.http import HttpResponseForbidden
from django.urls import reverse
from django.views.generic import FormView
from django.views.generic.detail import SingleObjectMixin
?
class AuthorInterest(SingleObjectMixin, FormView):
  template_name = 'books/author_detail.html'
  form_class = AuthorInterestForm
  model = Author
?
  def post(self, request, *args, **kwargs):
      if not request.user.is_authenticated:
          return HttpResponseForbidden()
      self.object = self.get_object()
      return super().post(request, *args, **kwargs)
?
  def get_success_url(self):
      return reverse('author-detail', kwargs={'pk': self.object.pk})

最后,我们以一种新的AuthorDetail观点将它们组合在一起。我们已经知道,调用as_view()基于类的视图会使我们的行为与基于函数的视图完全相同,因此我们可以在两个子视图之间进行选择。

当然,您可以通过传递关键字参数as_view()的方式与在URLconf中传递 方式相同,例如,如果您希望该AuthorInterest行为也出现在另一个URL上,但使用不同的模板:

from django.views import View
?
class AuthorDetail(View):
?
  def get(self, request, *args, **kwargs):
      view = AuthorDisplay.as_view()
      return view(request, *args, **kwargs)
?
  def post(self, request, *args, **kwargs):
      view = AuthorInterest.as_view()
      return view(request, *args, **kwargs)

此方法还可以与任何其他直接从View或继承的常规基于类的视图或您自己的基于类的视图一起使用 TemplateView,因为它可以使不同的视图尽可能地分开。

当您想多次执行相同的操作时,基于类的视图就会大放异彩。假设您正在编写一个API,并且每个视图都应返回JSON而不是呈现的HTML。

我们可以创建一个mixin类以在所有视图中使用,一次处理到JSON的转换。

例如,JSON混合可能看起来像这样:

from django.http import JsonResponse

class JSONResponseMixin:
    """
    A mixin that can be used to render a JSON response.
    """
    def render_to_json_response(self, context, **response_kwargs):
        """
        Returns a JSON response, transforming 'context' to make the payload.
        """
        return JsonResponse(
            self.get_data(context),
            **response_kwargs
        )

    def get_data(self, context):
        """
        Returns an object that will be serialized as JSON by json.dumps().
        """
        # Note: This is *EXTREMELY* naive; in reality, you'll need
        # to do much more complex handling to ensure that arbitrary
        # objects -- such as Django model instances or querysets
        # -- can be serialized as JSON.
        return context

注意:请查看序列化Django对象文档,以获取有关如何正确将Django模型和查询集正确转换为JSON的更多信息。

这个mixin提供了render_to_json_response()一种与签名相同的方法render_to_response()。要使用它,我们需要将其混入一个TemplateView例如,并重写 render_to_response()以render_to_json_response()代替调用:

from django.views.generic import TemplateView

class JSONView(JSONResponseMixin, TemplateView):
    def render_to_response(self, context, **response_kwargs):
        return self.render_to_json_response(context, **response_kwargs)

同样,我们可以将我们的mixin与通用视图之一一起使用。我们可以DetailView通过JSONResponseMixin与django.views.generic.detail.BaseDetailView– 混合来制作自己的版本(混合 了 DetailView模板渲染之前的行为):

from django.views.generic.detail import BaseDetailView

class JSONDetailView(JSONResponseMixin, BaseDetailView):
    def render_to_response(self, context, **response_kwargs):
        return self.render_to_json_response(context, **response_kwargs)

然后可以以与其他任何视图相同的方式部署此视图 DetailView,并且行为完全相同-除了响应的格式。

如果你想成为真正的冒险,你甚至可以混合使用一个 DetailView子类,它能够返回两个 HTML和JSON的内容,根据不同的HTTP请求,的某些属性,如查询参数或HTTP标头。混合使用 JSONResponseMixin和和 SingleObjectTemplateResponseMixin,并render_to_response() 根据用户请求的响应类型覆盖的实现, 以采用适当的呈现方法:

from django.views.generic.detail import SingleObjectTemplateResponseMixin

class HybridDetailView(JSONResponseMixin, SingleObjectTemplateResponseMixin, BaseDetailView):
    def render_to_response(self, context):
        # Look for a 'format=json' GET argument
        if self.request.GET.get('format') == 'json':
            return self.render_to_json_response(context)
        else:
            return super().render_to_response(context)

由于Python解决方法重载的方式,对的调用 super().render_to_response(context)最终调用的 render_to_response() 实现TemplateResponseMixin。

详情参考: https://docs.djangoproject.com/en/3.0/



标签:Django

返回前面的内容

相关阅读 >>

基于类的视图

使用asgi进行部署

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

基于类的视图简介

Django 模板

使用wsgi进行部署

Django的缓存框架

使用基于类的视图进行表单处理

错误报告

Django 处理http请求

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




打赏

取消

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

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

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

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

评论

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