Django 2.1.3 视图层 CBV介绍

CBV | 总目录 | 内置显示视图


基于类的视图提供了另一种将视图实现为Python对象而不是函数的方法。它们不替换基于函数的视图,但与基于函数的视图相比具有一定的差异和优势:

  • 与特定HTTP方法(GET,POST等)相关的代码组织可以通过单独的方法而不是条件分支来解决。
  • 诸如mixins(多重继承)之类的面向对象技术可用于将代码分解为可重用组件。

1. GV,CBV和CBGV的关系和历史

译者注:这里GV为通用视图(generic views)的简写,CBV为基于类的视图(class-based views)的简写,CBGV就是class-based generic views的简写。

在开始时只有视图函数联系,Django给你的函数传递了 HttpRequest 并且预期返回一个 HttpResponse。这是Django提供的事情。

早期就认识到在视图的开发中有很多共同的风格和模式。引入了基于函数的通用视图(FBGV)来抽象这些模式并简化常见案例的视图开发。

FBGV的问题在于,虽然它们很好地涵盖了简单的情况,但是除了一些简单的配置选项之外,没有办法扩展或定制它们,限制了它们在许多实际应用程序中的用途。

CBV与FBGV具有相同的目标,以使视图开发更容易。但是,通过使用mixins实现解决方案的方式提供了一个工具包,使得CBGV比FBGV更具可扩展性和灵活性。

如果您在过去尝试过FBGV并且发现它们很匮乏,那么您不应该将CBGV简单地视为基于类的等效视图,而应该将其视为解决通用视图的原始问题的新方法解决。

Django用于构建CBGV的基类和混合的工具包是为了最大的灵活性而构建的,因此在默认方法实现和属性的形式中有许多钩子,在最简单的使用中你不太可能会关注它们。例如,之前的做法是限制你只能用基于类的form_class属性,而默认实现中使用一个get_form方法,它调用get_form_class方法,该方法在其默认实现中只返回form_class类的属性。这为您提供了几个选项,用于指定要使用的表单,从简单属性到完全动态的可调用挂钩。这些选项似乎为简单情况增加了空洞复杂性,但如果没有它们,更先进的设计将受到限制。

2. 使用CBV

从本质上讲,CBV允许您使用不同的类实例方法响应不同的HTTP请求方法,而不是在单个视图函数中使用条件分支代码。

那么在GET视图函数中处理HTTP的代码看起来像这样:

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

在CBV中,这将变为:

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

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

因为Django的URL解析器期望将请求和关联的参数发送到可调用的函数而不是类,所以CBV具有 as_view()类方法,该类方法返回当请求到达匹配关联模式的URL时可以调用的函数。该函数创建类的实例并调用其 dispatch()方法。dispatch查看请求以确定它是否为GET,POST等等,并将请求中继到匹配方法(如果已定义),或者如果匹配不到则引发 HttpResponseNotAllowed

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

值得注意的是,您的方法返回的内容与从基于函数的视图返回的内容相同,即某种形式的 HttpResponse。这意味着 http快捷函数TemplateResponse对象在CBV中有效。

虽然最小的CBV不需要任何类属性来执行其工作,但类属性在许多基于类的设计中很有用,并且有两种方法来配置或设置类属性。

第一种是标准Python的子类方式,并覆盖子类中的属性和方法。因此,如果您的父类具有如下属性 greeting:

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

class GreetingView(View):
    greeting = "Good Day"

    def get(self, request):
        return HttpResponse(self.greeting)

您可以在子类中覆盖它:

class MorningGreetingView(GreetingView):
    greeting = "Morning to ya"

另一个方式是将类属性配置为函数 as_view() 的关键字参数 :

urlpatterns = [
    path('about/', GreetingView.as_view(greeting="G'day")),
]

注解
虽然为每个分派给它的请求都实例化了类,但通过as_view()入口点设置的类属性在导入URL时只配置一次。

3. 使用mixins

Mixins是多重继承的一种形式,其中可以组合多个父类的行为和属性。

例如,在CBGV中,有一个mixin叫做 TemplateResponseMixin 其主要目的是定义 render_to_response() 方法。当与基类View 的行为结合使用时,结果是一个TemplateView 类,它将请求分配给适当的匹配方法(在View基类中定义的行为),并且具有 render_to_response()方法 ,此方法使用 template_name 属性返回TemplateResponse 对象(在TemplateResponseMixin中定义的行为)。

Mixins是在多个类中重用代码的绝佳方法,但它们需要一些成本。你的代码散布在mixins中的次数越多,读取子类就越难,并且知道它究竟在做什么,如果你继承了的子类具有深度继承树,那么知道哪些方法可以覆盖就更难了。

另请注意,您只能从一个通用视图继承 - 也就是说,只有一个View父类可以继承,其余的(如果有的话)应该是mixins。尝试从多个继承View的类进行继承- 例如,尝试在列表顶部使用表单并组合ProcessFormViewListView - 将无法按预期工作。

4. 使用CBV处理表单

处理表单的FBV可能如下所示:

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import MyForm

def myview(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')
    else:
        form = MyForm(initial={'key': 'value'})

    return render(request, 'form_template.html', {'form': form})

类似的CBV可能如下所示:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from .forms import MyForm

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')

        return render(request, self.template_name, {'form': form})

这是一个非常简单的情况,但您可以看到,您可以选择通过覆盖任何类属性来自定义此视图,例如 form_class,通过URLconf配置,或子类化和覆盖一个或多个方法(或两者一起 )。

5. 装饰CBV

CBV的扩展不仅限于使用mixins。您也可以使用装饰器。由于CBV不是函数,因此根据您是否正在使用as_view()或创建子类,装饰它们的工作方式不同。

5.1 在URLconf中装饰

装饰CBV的最简单方法是装饰as_view()方法的结果。最简单的方法是在部署视图的URLconf中:

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
]

此方法基于每个实例应用装饰器。如果您希望对视图的每个实例进行修饰,则需要采用不同的方法。

5.2 装饰类

要装饰每个CBV实例,您需要装饰类的定义本身。为此,您可以将装饰器应用于类的 dispatch() 方法。

类上的方法与独立函数并不完全相同,因此您不能只将函数装饰器应用于该方法 - 您需要先将其转换为方法装饰器。method_decorator 装饰器用来将一个函数装饰器转换为一个方法装饰器,使得它可以在一个实例方法中使用。例如:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

或者,更简洁地说,您可以改为装饰类,并将要装饰的方法的名称作为关键字参数传递给name:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

如果在一些地方使用了一组常用装饰器,则可以定义装饰器的列表或元组,并使用它而不是多次调用method_decorator() 。这两个类是等价的:

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

装饰器将按照传递给装饰器的顺序处理请求。在示例中,never_cache()将在 login_required()之前处理请求。

在此示例中,每个ProtectedView实例都将具有登录保护。

注解
method_decorator传递*args和**kwargs 作为类的装饰方法的参数。如果您的方法不接受一组兼容的参数,则会引发 TypeError异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值