基于类的通用视图(Class-based generic views)
在web开发中,最令人头痛的就是一遍又一遍的重复固定的模式。在解决了模板层面和模型层面的重复代码之痛之后,Django使用通用视图来解决视图层面的代码重复。
扩展通用视图
毫无疑问通用视图可以大幅度地加速web开发,但是在许多项目中,总是有通用视图不够用的时候。确实,现今的Django开发者们需要知道如何让通用视图可以处理更多的纷繁复杂的场景。
这是1.3版本的通用视图被重新设计的原因之一。以前,通用视图只是一些带有一些扑朔迷离选择的视图函数,现在更加推荐的方法是通过继承、重写属性或方法来扩展视图函数,而不是在URLconf中进行大量的配置。
对象的通用视图
TemplateView确实不错,但是Django的通用视图真正大显身手的场景是在展现数据库内容的时候。因为这是如此普遍的任务,Django提供了一系列内置的通用视图来使编写展示对象细节的视图变得如此灵活、简单。
让我们先来看一看展示一系列对象和单个对象的一些例子,下面是将要用到的模型:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
# models.pyfrom django.db import modelsclass Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Meta: ordering = ["-name"] def __unicode__(self): return self.nameclass Author(models.Model): salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='author_headshots') def __unicode__(self): return self.nameclass Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') publisher = models.ForeignKey(Publisher) publication_date = models.DateField() |
现在我们定义一个视图:
|
1
2
3
4
5
6
|
# views.pyfrom django.views.generic import ListViewfrom books.models import Publisherclass PublisherList(ListView): model = Publisher |
然后将它加到urls中:
|
1
2
3
4
5
6
7
|
# urls.pyfrom django.conf.urls import patterns, urlfrom books.views import PublisherListurlpatterns = patterns('', url(r'^publishers/$', PublisherList.as_view()),) |
那就是全部我们需要编写的python代码。当然我们还需要写一个template,我们可以在视图中添加一个template_name属性来指明模板。如果我们不指明的话,Django将会自己推断使用哪个模板,在这个例子中,Django将会推断要使用“books/publisher_list.html”,其中“books”是定义模型的app的名字,“publisher”是模型名字的小写。
注意:自动寻找模板的功能仅在django.template.loaders.app_diretories.Loader被启用的情况下可行(在TEMPLATE_LOADERS中设置)
这个模板将会使用一个叫object_list的变量来渲染,这个变量包含了所有的publisher对象。一个简单的模板看起来可能会像这样:
|
1
2
3
4
5
6
7
8
9
10
|
{% extends "base.html" %}{% block content %} <h2>Publishers</h2> <ul> {% for publisher in object_list %} <li>{{ publisher.name }}</li> {% endfor %} </ul>{% endblock %} |
这就是所有要做的了。那些很酷的通用视图都是通过改写内置通用视图的属性来的,你可以在通用视图参考中查看所有通用视图的细节。本文剩下的部分将聚焦几个你可能会经常使用的扩展通用视图的方法。
让template context变得更加友好
你可能已经注意到了,在我们例子中,所有的publisher都保存在变量object_list中,这是默认的变量名。当然你也可用另一个:publisher_list,如你所想,publisher是模型的名字,这是Django为你提供的第二选择。如果你仍然觉得这个名字不完美,你也可以自己命名,方法是在通用视图中添加context_object_name变量:
|
1
2
3
4
5
6
7
|
# views.pyfrom django.views.generic import ListViewfrom books.models import Publisherclass PublisherList(ListView): model = Publisher context_object_name = 'my_favourite_publishers' |
取了一个这么好听的名字,你设计模板的搭档一定爱死你了。
增加额外的context
通常你需要展示一些通用视图没有提供的信息,比如展示所有每个publisher写的所有的书。DetailView通用视图给context提供publisher信息,那我们如何在模板中获得额外的信息呢?
答案就是继承DetailView,并重写get_context_data方法。默认的方法只是简单的展示对象,但是我们可以重写让它展示更多:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from django.views.generic import DetailViewfrom books.models import Publisher, Bookclass PublisherDetail(DetailView): model = Publisher def get_context_data(self, **kwargs): # Call the base implementation first to get a context context = super(PublisherDetail, self).get_context_data(**kwargs) # Add in a QuerySet of all the books context['book_list'] = Book.objects.all() return context |
对象的视图子集
让我们近距离地观察一下model参数。model参数指明了我们将操作的数据库模型,所有的通用视图都有这个参数。但是这个参数并不是指明对象的唯一方法。你也可以使用queryset参数来指明一系列对象:
|
1
2
3
4
5
6
7
|
from django.views.generic import DetailViewfrom books.models import Publisher, Bookclass PublisherDetail(DetailView): context_object_name = 'publisher' queryset = Publisher.objects.all() |
其实model=Publisher只是queryset=Publisher.object.all()的简写。你可以用queryset来对象更加精确,举个例子,你可能想要按出版时间例举一系列书:
|
1
2
3
4
5
6
|
from django.views.generic import ListViewfrom books.models import Bookclass BookList(ListView): queryset = Book.objects.order_by('-publication_date') context_object_name = 'book_list' |
这是一个简单的例子,但是足以说明这个主意不错。如果你想要按照特定的作者展示一系列书,这个技术同样可以帮到你:
|
1
2
3
4
5
6
7
8
|
from django.views.generic import ListViewfrom books.models import Bookclass AcmeBookList(ListView): context_object_name = 'book_list' queryset = Book.objects.filter(publisher__name='Acme Publishing') template_name = 'books/acme_list.html' |
注意这个例子中我们指明了模板名称,否则它将使用我们上面例子的模板。
动态过滤
另一个需求可能是你想要通过URL中的参数来过滤对象。在上个例子中,我们将publisher的名字硬编码在URLconf中,如果您想展示任意publisher的书籍又该怎么办呢?
很简单,我们可以重写ListView的get_queryset()方法。
这里,我们使用命名组来获得URL中的参数:
|
1
2
3
4
5
6
|
# urls.pyfrom books.views import PublisherBookListurlpatterns = patterns('', (r'^books/([\w-]+)/$', PublisherBookList.as_view()),) |
然后我们来写PublisherBookList视图:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
# views.pyfrom django.shortcuts import get_object_or_404from django.views.generic import ListViewfrom books.models import Book, Publisherclass PublisherBookList(ListView): template_name = 'books/books_by_publisher.html' def get_queryset(self): self.publisher = get_object_or_404(Publisher, name=self.args[0]) return Book.objects.filter(publisher=self.publisher) |
你看,在queryset中增加逻辑是很简单的。我们也可以在context中同时增加publisher,这样一来我们就可以在模板中使用它了:
|
1
2
3
4
5
6
7
8
|
# ...def get_context_data(self, **kwargs): # Call the base implementation first to get a context context = super(PublisherBookList, self).get_context_data(**kwargs) # Add in the publisher context['publisher'] = self.publisher return context |
干一些额外的活
最后我们关注一下在调用通用视图的前后做一些额外的工作。
想象一下我们的Author模型有一个last_accessed字段来保存上次该作者被访问的时间:
|
1
2
3
4
5
6
7
8
|
# models.pyclass Author(models.Model): salutation = models.CharField(max_length=10) name = models.CharField(max_length=200) email = models.EmailField() headshot = models.ImageField(upload_to='author_headshots') last_accessed = models.DateTimeField() |
当然通用视图DetaiView对此一无所知,不过我们可以再次定制一个视图来这个字段保持更新。
首先,我们需要在URLconf中增加一个作者信息来指向视图:
|
1
2
3
4
5
6
|
from books.views import AuthorDetailViewurlpatterns = patterns('', #... url(r'^authors/(?P<pk>\d+)/$', AuthorDetailView.as_view(), name='author-detail'),) |
然后我们编写新的视图。get_object一个取回对象的方法,这样我们就可以轻松地重写它:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from django.views.generic import DetailViewfrom django.shortcuts import get_object_or_404from django.utils import timezonefrom books.models import Authorclass AuthorDetailView(DetailView): queryset = Author.objects.all() def get_object(self): # Call the superclass object = super(AuthorDetailView, self).get_object() # Record the last accessed date object.last_accessed = timezone.now() object.save() # Return the object return object |
基于类的通用视图(Class-based generic views)的更多相关文章
- 用基于类的通用视图处理表单(Class-based generic views)
处理表单通常包含3步: 初始化GET(空白的后者预填充的表单) POST非法数据(通常重新显示带有错误信息的表单) POST合法数据(提交数据并重定向) 为了将你从这些烦人的重复步骤中解救出来,Dja ...
- Django 1.6 基于类的通用视图
Django 1.6 基于类的通用视图 最初 django 的视图都是用函数实现的,后来开发出一些通用视图函数,以取代某些常见的重复性代码.通用视图就像是一些封装好的处理器,使用它们的时候只须要给出特 ...
- Django 基于类的通用视图
在早期,我们认识到在视图开发过程中有共同的用法和模式.这时我们引入基于函数的通用视图来抽象这些模式以简化常见情形的视图开发. 基于函数视图的用法有以下三种: def index(request): r ...
- Django——django1.6 基于类的通用视图
最初 django 的视图都是用函数实现的,后来开发出一些通用视图函数,以取代某些常见的重复性代码.通用视图就像是一些封装好的处理器,使用它们的时候只须要给出特定的参数集即可,不必关心具体的实现.各种 ...
- 介绍——基于类的视图(class-based view)
刚开始的时候,django只有基于函数的视图(Function-based views).为了解决开发视图中繁杂的重复代码,基于函数的通用视图( Class-based generic views) ...
- Django——基于类的视图(class-based view)
刚开始的时候,django只有基于函数的视图(Function-based views).为了解决开发视图中繁杂的重复代码,基于函数的通用视图( Funcation-based generic vie ...
- django通用视图(类方法)
这周是我入职的第一周,入职第一天看到嘉兴大佬的项目代码.视图中有类方法,我感到很困惑. 联想到之前北京融360的电话面试,问我有无写过类方法……看来有必要了解下视图的类方法,上网搜了很多,原来这就是所 ...
- Django入门与实践-第22章:基于类的视图
http://127.0.0.1:8000/boards/1/topics/2/posts/2/edit/ http://127.0.0.1:8000/ #boards/views.py from d ...
- Django——基于类的视图源码分析 一
基于类的视图(Class-based view)是Django 1.3引入的新的视图编写方式,用于取代以前基于函数(Function-based)方式. 借助于OO和Python中方便的多重继承特性, ...
随机推荐
- (转)IDE 而言,是 Xcode 的技术比较先进还是 Visual Studio?
李如一他们弄得那个IT公论,最近有一期是吐槽ObjC的.吐到最后, @涛 吴 说,理想的用户界面语言应该是界面的描述和逻辑分开的,想了半天举不出例子来,其实说的不就是WPF吗?还在用Interface ...
- 【转载】最长回文字符串(manacher算法)
原文转载自:http://blog.csdn.net/lsjseu/article/details/9990539 偶然看见了人家的博客发现这么一个问题,研究了一下午, 才发现其中的奥妙.Stupid ...
- 为什么C++编译器不能支持对模板的分离式编译
首先,一个编译单元(translation unit)是指一个.cpp文件以及它所#include的所有.h文件,.h文件里的代码将会被扩展到包含它的.cpp文件里,然后编译器编译该.cpp文件为一个 ...
- C语言函数篇(三)函数参数高级设计
在内存空间中, 1. 单位大小的数据 叫 数值 . 比如 int a; char b; ... 2. 由这些单位数据组合起来的内存, 称作 空间. 比如: 各种数组char/int a[10]; 结构 ...
- 部分和问题 南阳acm1058(递归+dfs)
部分和问题 时间限制:1000 ms | 内存限制:65535 KB 难度:2 描述 给定整数a1.a2........an,判断是否可以从中选出若干数,使它们的和恰好为K. 输入 首先, ...
- JAVA-数组或集合
哈哈,今天我们来讲解一下有关于一些数组 或者是集合的知识点 1.ArrayList,LinkedList,Vector的区别 ArrayList,LinkedList,Vector都是实现List接口 ...
- 20145202课后题,2.56&9.16
我做的是2.56题,要求我用多组值来测试show_bytes 9.16 主要是对局部性进行了一些分析. 实验楼里面是空的,我电脑上显示不出来,所以我就做了一些书上的习题. 第十章的所有题目都被学长做过 ...
- Android stadio 关联源码
有时候,你想在Android stadio 里看源码, 然后Android stadio 会提示你去下载. 但是下载完了之后,有时候stadio 还是不能看源码.后来,参考这位博客,搞完了. http ...
- 四大关键步骤掌握CloudOps模型
[TechTarget中国原创] 要让IT运维向云演进,企业必须拥抱自动化,并且改变资源预配的思考方式. 新涌现的术语CloudOps——云运维的简写,指代企业如何运行以及管理基于云的系统.并且,随着 ...
- 《算法》C++代码 Floyd
今天写写最短路径的Floyd算法(有翻译叫弗洛伊德,不过这奇葩翻译用来读读就好……). 这个算法的实质,广义来讲,其实是DP(动态规划).其实按说,算法应该先说说什么贪心.搜索.DP.二分之类的基本算 ...