基于类的视图提供了另一种将视图实现为Python对象而不是函数的方法。 它们不替换基于函数的视图,但与基于函数的视图相比具有一定的差异和优势:
GET
,POST
等)可以通过单独的方法而不是条件分支来解决。在开始时只有视图函数契约,Django传递了你的函数HttpRequest
并期望返回HttpResponse
。 这是Django提供的程度。
早期就认识到在视角发展中有共同的习语和模式。 引入了基于函数的通用视图来抽象这些模式并简化常见案例的视图开发。
基于函数的通用视图的问题在于,虽然它们很好地涵盖了简单的情况,但是除了一些简单的配置选项之外,没有办法扩展或定制它们,限制了它们在许多实际应用程序中的用途。
创建基于类的通用视图与基于函数的通用视图具有相同的目标,以使视图开发更容易。 但是,通过使用mixins实现解决方案的方式提供了一个工具包,使得基于类的通用视图比基于函数的对应视图更具可扩展性和灵活性。
如果您在过去尝试过基于函数的通用视图并且发现它们缺乏,那么您不应该将基于类的通用视图简单地视为基于类的等效视图,而应该将其视为解决通用视图的原始问题的新方法解决。
Django用于构建基于类的通用视图的基类和混合的工具包是为了最大的灵活性而构建的,因此在默认方法实现和属性的形式中有许多钩子,在最简单的使用中你不太可能关注它们案例。 例如,不是将您限制为form_class
的基于类的属性,而是实现使用get_form
方法,该方法调用get_form_class
方法,在其默认实现中,它只返回类的form_class
属性。 这为您提供了几个选项,用于指定要使用的表单,从简单属性到完全动态的可调用挂钩。 这些选项似乎为简单情况增加了空洞复杂性,但如果没有它们,更先进的设计将受到限制。
从本质上讲,基于类的视图允许您使用不同的类实例方法响应不同的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()
方法。 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
对象在基于类的视图中有效。
虽然最小的基于类的视图不需要任何类属性来执行其工作,但类属性在许多基于类的设计中很有用,并且有两种方法来配置或设置类属性。
第一种是子类的标准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"
另一个选项是将类属性配置为URLconf中as_view()
调用的关键字参数:
urlpatterns = [
path('about/', GreetingView.as_view(greeting="G'day")),
]
Note
虽然为每个分派给它的请求实例化了类,但是通过as_view()
入口点设置的类属性在导入URL时只配置一次。
Mixins是多重继承的一种形式,其中可以组合多个父类的行为和属性。
例如,在基于通用类的视图中,有一个名为TemplateResponseMixin
的mixin,其主要目的是定义方法render_to_response()
。
当与View
基类的行为结合使用时,结果是TemplateView
类,它将请求分配给适当的匹配方法(视图中定义的行为
基类),它有一个render_to_response()
方法,它使用template_name
属性返回TemplateResponse
对象(一种行为)在TemplateResponseMixin
中定义。
Mixins是在多个类中重用代码的绝佳方法,但它们需要一些成本。 你的代码散布在mixins中的次数越多,读取子类就越难以知道它正在做什么,并且如果你继承了一个具有一个类的东西,那么知道哪些方法可以覆盖哪些方法就更难了。深度继承树。
另请注意,您只能从一个通用视图继承 - 也就是说,只有一个父类可以从View
继承,其余的(如果有的话)应该是mixins。 尝试从多个继承自View
的类继承 - 例如,尝试使用列表顶部的表单并组合ProcessFormView
和ListView
- 无法按预期工作。
处理表单的基于函数的基本视图可能如下所示:
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})
类似的基于类的视图可能如下所示:
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})
This is a very simple case, but you can see that you would then have the option
of customizing this view by overriding any of the class attributes, e.g.
form_class
, via URLconf configuration, or subclassing and overriding one or
more of the methods (or both!).
The extension of class-based views isn’t limited to using mixins. You can also
use decorators. Since class-based views aren’t functions, decorating them works
differently depending on if you’re using as_view()
or creating a subclass.
The simplest way of decorating class-based views is to decorate the
result of the as_view()
method.
The easiest place to do this is in the URLconf where you deploy your view:
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())),
]
This approach applies the decorator on a per-instance basis. If you want every instance of a view to be decorated, you need to take a different approach.
To decorate every instance of a class-based view, you need to decorate
the class definition itself. To do this you apply the decorator to the
dispatch()
method of the class.
A method on a class isn’t quite the same as a standalone function, so
you can’t just apply a function decorator to the method – you need to
transform it into a method decorator first. The method_decorator
decorator transforms a function decorator into a method decorator so
that it can be used on an instance method. For example:
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)
Or, more succinctly, you can decorate the class instead and pass the name
of the method to be decorated as the keyword argument name
:
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
template_name = 'secret.html'
If you have a set of common decorators used in several places, you can define
a list or tuple of decorators and use this instead of invoking
method_decorator()
multiple times. These two classes are equivalent:
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'
The decorators will process a request in the order they are passed to the
decorator. In the example, never_cache()
will process the request before
login_required()
.
In this example, every instance of ProtectedView
will have login protection.
Note
method_decorator
passes *args
and **kwargs
as parameters to the decorated method on the class. If your method
does not accept a compatible set of parameters it will raise a
TypeError
exception.
Jan 17, 2018