您的位置:首页 > 编程语言 > Go语言

Django-深度分析Django基于类的视图(1)(翻译)

2017-08-27 08:02 543 查看
http://blog.thedigitalcatonline.com/blog/2013/10/28/digging-up-django-class-based-views-1/#.WaG7dJOGPBI

时间有限,只能简单翻译,略去一些无关紧要的细节。

本文是基于Django1.5版的,但是原理分析依旧非常有意义。

什么是基于类的视图?(CBV)

Django类也就是Python类。一个Django视图就是就是用来处理HTTP请求并返回HTTP响应的一段代码。不多不少,就这么简单。

略去关于Python类的解释。

Django 视图是有限状态机的完美例子。它接收请求,然后在内部经过一系列的处理步骤,最后产生一个响应,返还给用户。

下面让我们深入到一个实际的例子中去:

from django.views.generic.list import ListView
from articles.models import Article

class ArticleListView(ListView):
model = Article


这是一个关于ListView的例子。ListView是一个通用视图,用来处理一列对象(来自数据库)

例子中,假设articles是你的应用,Article是其中的一个model。

此段简单代码充分使用了继承的概念。这是怎么工作的呢?这个类是如何处理进入的请求并输出响应的呢?官方文档陈述:“当此视图执行时,self.object_list将会包含一个对象列表(一般是queryset,但不必是queryset),并且视图对其进行处理。这段话非常不易理解,尤其是对一个新手来说,已经不知所云了。

由于ArticleListView继承自ListView,你可以看看ListView的源代码了解其处理流程。当然看代码不是一件很容易的事儿,下面是我看代码所习得的内容,希望能帮助你理解。

URL分发器和视图

CBV是不能在URL分发器里直接使用的,所以你需要使用as_view()。as_view()基本上定义了一个方法,此方法用来将一个类(CBV)实例化并且调用方法dispatch(),然后as_view被返回给URL的分发器。作为用户,我们只关心此类的入口在哪里,也就是当一个请求来到了url链接,哪个方法被调用了。它就是diapatch()。

如果我们重新定义ArticleListView如下

from django.views.generic.list import ListView
from articles.models import Article

class ArticleListView(ListView):
model = Article

def dispatch(self, request, *args, **kwargs):
return super(ArticleListView, self).dispatch(request, *args, **kwargs)
此类并没有改变什么行为,它只是通过调用super()方法重载了dispatch()。这里理解*args和**kwargs这两个参数很重要。由于视图是被系统框架自动调用的,因此框架本身需要这些方法遵守一套特别的API,因此在在重载这些方法的时候,需要遵循一套原则。具体可以参考官方文档中关于CBV机理的文档。

dispatch()接收一个request参数,类型是HttpRequest,这里我们通过print将其打印到控制台。

from django.views.generic.list import ListView
from articles.models import Article

class ArticleListView(ListView):
model = Article

def dispatch(self, request, *args, **kwargs):
print(request)
return super(ArticleListView, self).dispatch(request, *args, **kwargs)
简而言之,这就是一个标准框架类的处理过程,当然也是Django CBV的标准流程:先继承一个已经定义好的类,找出哪个方法需要修改,根据规则对其进行重载,在重载代码中调用父类的代码。

GET请求

回到ArticleListView的例子。父类的dispatch()需要request对象的method的属性并且选择响应的处理器去处理这个请求。意思就是说,如果request.method是’GET‘,用HTTP的语言说就是,我需要读取一个资源,那么dispatch()就需要调用类的get()方法。

ListView的get()方法来自于BaseListView。它就是我们熟悉的函数视图。此函数的基本功能就是用self.queryset填充self.object_list属性。同时通过调用self.get_context_data()创建一个上下文(context),然后调用类版本的render_to_response(),也就是self.render_to_response()。

方法self.get_queryset()来自ListVIew的祖先MultipleObjectMixin,只是简单地通过调用queryset = self.model._default_manager.all()来获取。其中self.model的值就是来自model=Article。

这就是全部的过程!ArticleListView类从数据库中获取所有的Article对象,然后调用一个template同时给其提供一个上下文,其中包含了一个变量,object_list,其通过获取的对象列表进行实例化。

模版和上下文(template/context)

首先,当类调用self.render_to_response()时,它使用来在其父类TemplateResponseMixin的代码;此方法还调用其他的函数,但是其主要工作是通过模版和上下文创建一个响应。再一次,通过一系列的调用,模版来自self.template_name;尽管TemplateResponseMixin将其定义为None,但是ListView却通过其父类们神奇地返回了一个模版,并且其名字来自于指定的那个model。简单讲,ArticleListView定义了一个叫Article的model,然后就自动使用一个叫做article_list.html的模版。

我们可以改变这一行为吗?当然可以。这正是用类取代函数视图的原因:定制化视图的行为。下面我们更改类的定义如下:

from django.views.generic.list import ListView
from articles.models import Article

class ArticleListView(ListView):
model = Article
template_name = 'sometemplate.html'
这段代码含义是什么?当self.render_to_response()方法寻找self.template_name时,发现这个属性已经有了一个值。因此没有必要调用预先定义的模版,而sometemplate.html就用来渲染响应了。这是一种非常有用的面向对象的设计模式。

至于上下文,实际上它只是包含了一些值的一个字典,在你编译渲染模版时希望能够访问这些值。在上下文中的变量(同时也在模版中),具体什么格式和内容则完全取决于你自己。在使用CBV时,你会发现上下文中已经包含了由父类创建的一些变量,比如本例子中的object_list。如果你想用一个页面来显示所有的文章列表,同时希望添加一个值到上下文,该怎么办?

很容易!你要做的只是重载那个能产生上下文的函数并更改它的行为。比方说,我们想显示文章列表的同时,能够显示网站所有的读者的数量。假设Reader model已经存在了。

from django.views.generic.list import ListView
from articles.models import Article, Reader

class ArticleListView(ListView):
model = Article

def get_context_data(self, **kwargs):
context = super(ArticleListView, self).get_context_data(**kwargs)
context['readers'] = Reader.objects.count()
return context
当我们重载一个方法的时候,总是先调用父类的对应的方法,这样我们就能够获得我们期望的此方法的正常行为,然后再加上我们的定制。

结论

在本帖子中,我通过揭示一个请求如何通过get()一步一步到达视图,来揭开Django CBV和CBGV的神秘面纱。希望对你有所帮助。

在接下来的帖子中,我会探讨DetailView,用来显示对象详细信息的通用视图,如何创建定制化CBV,以及如何使用CBV处理表单,也就是POST请求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: