Introduction to class-based views基于类的视图介绍
基于类的视图提供了将视图实现为Python对象而不是函数的替代方法。 与基于功能的视图相比,它们不会替代基于功能的视图,但具有一定的差异和优势:
与特定HTTP方法
(GET
,POST
等)相关的代码的组织可以通过单独的方法而不是条件分支来解决。
可以使用面向对象的技术,如mixins(多重继承)
来将代码重新定义为可重用的组件。
The relationship and history of generic views, class-based views, and class-based generic views通用视图,基于类的视图和基于类的通用视图的关系和历史
一开始只有视图功能合约,Django将你的函数传递给一个HttpRequest
,并且期望返回一个HttpResponse
。这是Django提供的程度。
早期就认识到,在观点发展中发现了共同的习语和模式。引入基于功能的通用视图来抽象这些模式,并简化常见情况下的视图开发。
基于功能的通用视图的问题是,虽然它们很好地涵盖了简单的例子,但是没有办法扩展或定制它们超出一些简单的配置选项,限制了它们在许多现实应用程序中的有用性。
基于类的通用视图的创建与基于功能的通用视图具有相同的目标,以使视图开发更容易。然而,通过使用mixins
来实现解决方案的方式提供了一种工具包,使得基于类的通用视图比基于功能的通用视图更具可扩展性和灵活性。
如果您以前曾尝试过基于功能的通用视图,并发现它们缺少,那么您不应该将基于类的通用视图看作简单的基于类的等效内容,而应该是解决通用视图的原始问题的新方法。解决
Django用于构建基于类的通用视图的基类和混合的工具包是为了最大限度的灵活性而构建的,因此,以默认方法实现和属性的形式存在许多钩子,您不太可能在最简单的使用中关注案件例如,不是将您限制为form_class
的基于类的属性,而是使用get_form
方法,该方法调用一个get_form_class
方法,该方法在其默认实现中只返回该类的form_class
属性。这为您提供了几个选项,用于指定要使用的表单,从简单属性到完全动态的可调用挂接。这些选项似乎在简单的情况下增加了空洞的复杂性,但没有这些选项,更高级的设计将受到限制。
Using class-based views使用基于类的视图
基于类的视图允许您使用不同的类实例方法来响应不同的HTTP请求方法,而不是在单个视图函数中有条件地分支代码。
那么处理视图函数中的HTTP GET
的代码将如下所示:
from django.http import HttpResponse
def my_view(request):
if request.method == 'GET':
# <view logic>
return HttpResponse('result')
在基于类的观点中,这将成为:
from django.http import HttpResponse
from django.views import View
class MyView(View):
def get(self, request):
# <view logic>
return HttpResponse('result')
因为Django
的URL解析器希望将请求和关联的参数发送到可调用函数,而不是类,基于类的视图具有一个as_view()
类方法,该方法返回一个函数,当一个请求到达时匹配相关联的URL模式。该函数创建一个类的实例并调用其dispatch()
方法。调度查看请求以确定它是否是GET
,POST
等,并将请求中继到匹配方法(如果已定义),或者引发HttpResponseNotAllowed
(如果不是):
# urls.py
from django.conf.urls import url
from myapp.views import MyView
urlpatterns = [
url(r'^about/$', MyView.as_view()),
]
值得注意的是,您的方法返回与从基于函数的视图(即某种形式的HttpResponse
)返回的相同。 这意味着http快捷方式或TemplateResponse
对象在基于类的视图中是有效的。
虽然最小的基于类的视图不需要任何类属性来执行其作业,但类属性在许多基于类的设计中是有用的,并且有两种方式来配置或设置类属性。
第一个是在子类中子类化和覆盖属性和方法的标准Python方法。 所以如果你的父类有这样的属性问候语:
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"
另一个选择是将类属性配置为URLconf中as_view()
调用的关键字参数:
注意
当您的类被实例化为分配给它的每个请求时,通过as_view()入口点设置的类属性在配置URL时只被配置一次。
Using mixins
Mixins
是一种多重继承的形式,可以组合多个父类的行为和属性。
例如,在通用类视图中,有一个名为TemplateResponseMixin
的mixin
,其主要目的是定义render_to_response()
方法。当结合View基类
的行为时,结果是一个TemplateView类
,它将请求分配给适当的匹配方法(在View基类中定义的一个行为),并且具有使用template_name
属性的render_to_response()
方法返回TemplateResponse对象
(TemplateResponseMixin
中定义的一个行为)。
Mixins是在多个课程中重用代码的绝佳方法,但是它们会带来一些代价。你的代码分散在混合体中越多,阅读一个小孩类就越难,知道它正在做什么,而且越难知道要重写哪个mixin的方法,如果你是将深承继树。
还要注意,您只能从一个通用视图继承,也就是说,只有一个父类可以从View继承,而其余的(如果有的话)应该是mixins。尝试从继承自View的多个类继承 - 例如,尝试使用列表顶部的表单并结合ProcessFormView
和ListView
- 将无法正常工作。
Handling forms with class-based views处理基于类视图的表单
处理表单的基本的基于功能的视图可能如下所示:
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})
A similar class-based view might look like:
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配置
,或子类化和覆盖一个或多个方法(或两者!)。
Decorating class-based views装饰基于类的视图
基于类的视图的扩展不限于使用mixins
。 您也可以使用装饰。 由于基于类的视图不是函数,根据您是否使用as_view()
或创建子类,装饰它们的工作方式不同。
Decorating in URLconf在URLconf中装饰
装饰基于类的视图的最简单方法是装饰as_view()
方法的结果。 最简单的方法是在您部署视图的URLconf
中:
from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView
from .views import VoteView
urlpatterns = [
url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))),
url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())),
]
这种方法在每个实例的基础上应用装饰器。 如果你希望每个视图的实例被装饰,你需要采取不同的方法。
Decorating the class装饰类
要装饰基于类的视图的每个实例,您需要装饰类定义本身。 要做到这一点,你可以将装饰器应用到类的dispatch()
方法中。
类上的一个方法与独立的函数不完全相同,所以你不能仅仅将一个函数装饰器应用到这个方法中 - 你需要先将它转换成一个方法装饰器。method_decorator
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(ProtectedView, self).dispatch(*args, **kwargs)
或者,更简洁,您可以装饰类,并将要装饰的方法的名称作为关键字参数名称传递:
@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()
之前处理请求。