为何要使用通用视图呢,一步步地定义url,写函数取数据,渲染模板不也能完成任务么?仅仅因为这些函数比较高级,我们就得学,为了证明自己django用的熟练?当然不是啦,djangobook中有一段话很好地回答了why的问题:

  • 在最坏的情况下, Web开发是一项无聊而且单调的工作(大多情况还是很有趣的啦~)。在视图的层面上,也经历着这种令人厌倦的事情。Django的通用视图可以减少这些痛苦。 它抽象出一些在视图开发中常用的代码和模式,这样就可以在无需编写大量代码的情况下,快速编写出常用的数据视图。 事实上,前面章节中的几乎所有视图的示例都可以在通用视图的帮助下重写。

‘技术让生活更美好’,我们只是想拥有更多的时间陪喜欢的人,做喜欢的事,更多的时间出游,看书,睡懒觉。所以我们需要更高级的工具来节省时间,提高效率。那么我们还有什么理由不好好学习高级工具呢。
好的既然值得学习,我们趁热打铁进入内容部分吧。

Djangobook第11章中说内建通用视图可以实现如下功能:

  • 完成常用的简单任务: 重定向到另一个页面以及渲染一个指定的模板。
  • 显示列表和某个特定对象的详细内容页面。
  • 呈现基于日期的数据的年/月/日归档页面,关联的详情页面,最新页面。像是典型的新闻报纸归档。

这些都是极其常见的需求,尤其是在一个新闻发布系统中,而大多企业建站需要的仅仅是个新闻发布系统而已。

###使用举例:

####direct_to_template

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#url.py
#coding=utf-8
#呈现静态“关于”页面

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template

urlpatterns = patterns('',
    (r'^about/$', direct_to_template, {
        'template': 'about.html'
    })
)

direct_to_template视图仅仅是直接从传递过来的额外参数获取信息并用于渲染视图。
这样就不用为了呈现一个静态页,mvc都得走一遍。
direct_to_template 。 因为它返回一个HttpResponse对象,我们只需要简单的返回它就好了。例如可以这样:

1
2
3
4
5
def about_pages(request, page):
    try:
        return direct_to_template(request, template="about/%s.html" % page)
    except TemplateDoesNotExist:
        raise Http404()

其他通用视图也是同理。

####对象的通用视图list_detail(使用djangobook中的例子)

 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
#models.py
class 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()

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ['name']

#urls.py
from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher

publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_name': 'publisher_list_page.html',#
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

)

那么通用视图究竟有哪些可选的参数呢,可以参考这里Appendix C: Generic View Reference

1
2
3
4
object_list(request, queryset, paginate_by=None, page=None, allow_empty=True, 
        template_name=None, template_loader=loader, extra_context=None, 
        context_processors=None, template_object_name='object', mimetype=None
)

特别说明下context字典项会被模板使用。如果不指定模板,该函数将使用[app_name]/[model_name]_list.html作为模板,这很有些约定优于配置味道。

object_list函数创建的context中会包含许多字典项: 深入Django(1): 通用视图 (generic views)这篇博客给出了详细的中文注释:

  • object_list 要显示的对象的list
  • is_paginated 是否分页
  • results_per_page 如果分页,存储每页记录数
  • has_next 是否有下一页
  • has_previous 是否有上一页
  • page 当前页码
  • next 下一页
  • previous 上一页
  • pages 总页数
  • hits 总条目数
  • last_on_page 本页最后录一条记录的序数(从1开始)
  • first_on_page 本页第一条记录的序数(从1开始)
  • page_range 页码范围的列表

如果这些context字典项不能满足你的需要,还可以通过指定extra_context参数,传入一个字典,该字典中的内容会被合并到context字典中。我们接下去说如何扩展通用视图,go on ~

####扩展通用视图

在我们最开始举的publisher例子中(来自djanobook),可以这样扩展context字典

1
2
3
4
5
6
publisher_info = {
    'queryset': Publisher.objects.all(),
    'template_object_name': 'publisher',
    'extra_context': {'book_list': Book.objects.all()}#这是错的!!使用下面一个
    'extra_context': **{'book_list': Book.objects.all}
}

这样就把一个 {{ book_list }} 变量放到模板的context中。 这个方法可以用来传递任意数据 到通用视图模板中去,这是非常方便的.
需要注意的是all()不可立刻触发,应当由请求事件来触发,因此作为函数传递,不写().这是一个函数的引用,并没有真正调用它(通用视图将会在渲染时调用它)。
还有就是选项’queryset’仅仅是普通的queryset,你可以用对象的子集,比如'queryset':Publisher.objects.filter(name='Harry')

###当数据过滤比较复杂时

这是我们可以把通用视图写到函数里,前头说过通用视图返回一个HttpResponse对象,我们提供参数返回它就好,故而用在views中是没问题的,像酱紫:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#urls.py
urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    **(r'^books/(\w+)/$', books_by_publisher),**
)

#views.py
from django.shortcuts import get_object_or_404
from django.views.generic import list_detail
from mysite.books.models import Book, Publisher

def books_by_publisher(request, name):

    # Look up the publisher (and raise a 404 if it can't be found).
    publisher = get_object_or_404(Publisher, name__iexact=name)

    # Use the object_list view for the heavy lifting.
    return list_detail.object_list(
        request,
        queryset = Book.objects.filter(publisher=publisher),
        template_name = 'books/books_by_publisher.html',
        template_object_name = 'book',
        extra_context = {'publisher': publisher}
    )

我的个人习惯是直接查阅源码来得快些。推荐使用github的库内搜索功能,比如我们搜索django,之后在django项目中搜索object_list,这样每回遇到新的库,不一定非要下载到本地看代码样随时随地想起来都可以查阅,甚至火车晚点时,可以用手机看看源码。读源码也挺有意思的。许多大项目,源码都写得十分漂亮。

好了,你现在有了更高效的工具了,因此节约出来的时间可以陪小伙伴去玩耍啦

###Django提供的其他通用视图

我们用深入Django(1): 通用视图 (generic views)中归纳的:

分布在几个模块中:

  • django.views.generic.list_detail模块
  • object_list 显示模型对象列表
  • object_detail 显示单个模型对象
  • django.views.generic.create_update模块
  • create_object 创建模型对象
  • update_object 修改模型对象
  • delete_object 删除模型对象
  • django.views.generic.simple模块
  • direct_to_template 直接使用指定的模板渲染给定的context对象
  • redirect_to 重定向到指定的url
  • django.views.generic.date_based模块

这个模块主要处理“按时间查看存档”的功能,来源于新闻出版行业。具体包括:

  • archive_index 最顶级的归档,列出所有年份及指定数量的最新对象
  • archive_year 按年归档,列出所有拥有对象的月份
  • archive_month 按月归档,列出本月的所有对象,找到拥有对象的上一个、下一个月份
  • archive_week 按周归档,列出本周的所有对象
  • archive_day 按日归档,列出当天的所有对象,找到拥有对象的上一个、下一个日期
  • archive_today 当前日期(今天)的按日归档
  • object_detail 显示按照年/月/日/序号找到的对象

剩下的就是查阅文档,了解参数了。

我的个人习惯是直接查阅源码来得快些。推荐使用github的库内搜索功能,比如我们搜索django,之后在django项目中搜索object_list,这样每回遇到新的库,不一定非要下载到本地看代码样随时随地想起来都可以查阅,甚至火车晚点时,可以用手机看看源码。读源码也挺有意思的。许多大项目,源码都写得十分漂亮。

###参考资料: * djangobook第11章
* 深入Django(1): 通用视图 (generic views) * Appendix C: Generic View Reference