详解Django通用视图中的函数包装
用函数包装来处理复杂的数据过滤
另一个常见的需求是按URL里的关键字来过滤数据对象。之前,我们在URLconf中硬编码了出版商的名字,但是如果我们想用一个视图就显示某个任意指定的出版商的所有书籍,那该怎么办呢?我们可以通过对object_list通用视图进行包装来避免写一大堆的手工代码。按惯例,我们先从写URL配置开始:
urlpatterns=patterns('', (r'^publishers/$',list_detail.object_list,publisher_info), **(r'^books/(\w+)/$',books_by_publisher),** )
接下来,我们写books_by_publisher这个视图:
fromdjango.shortcutsimportget_object_or_404 fromdjango.views.genericimportlist_detail frommysite.books.modelsimportBook,Publisher defbooks_by_publisher(request,name): #Lookupthepublisher(andraisea404ifitcan'tbefound). publisher=get_object_or_404(Publisher,name__iexact=name) #Usetheobject_listviewfortheheavylifting. returnlist_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} )
这样写没问题,因为通用视图就是Python函数。和其他的视图函数一样,通用视图也是接受一些参数并返回HttpResponse对象。因此,通过包装通用视图函数可以做更多的事。
注意
注意在前面这个例子中我们在extra_context中传递了当前出版商这个参数。
处理额外工作
我们再来看看最后一个常用模式:
想象一下我们在Author对象里有一个last_accessed字段,我们用这个字段来记录最近一次对author的访问。当然通用视图object_detail并不能处理这个问题,但是我们仍然可以很容易地编写一个自定义的视图来更新这个字段。
首先,我们需要在URL配置里设置指向到新的自定义视图:
frommysite.books.viewsimportauthor_detail urlpatterns=patterns('', #... **(r'^authors/(?P<author_id>\d+)/$',author_detail),** #... )
接下来写包装函数:
importdatetime fromdjango.shortcutsimportget_object_or_404 fromdjango.views.genericimportlist_detail frommysite.books.modelsimportAuthor defauthor_detail(request,author_id): #DelegatetothegenericviewandgetanHttpResponse. response=list_detail.object_detail( request, queryset=Author.objects.all(), object_id=author_id, ) #Recordthelastaccesseddate.Wedothis*after*thecall #toobject_detail(),notbeforeit,sothatthiswon'tbecalled #unlesstheAuthoractuallyexists.(Iftheauthordoesn'texist, #object_detail()willraiseHttp404,andwewon'treachthispoint.) now=datetime.datetime.now() Author.objects.filter(id=author_id).update(last_accessed=now) returnresponse
注意
除非你添加last_accessed字段到你的Author模型并创建books/author_detail.html模板,否则这段代码不能真正工作。
我们可以用同样的方法修改通用视图的返回值。如果我们想要提供一个供下载用的纯文本版本的author列表,我们可以用下面这个视图:
defauthor_list_plaintext(request): response=list_detail.object_list( request, queryset=Author.objects.all(), mimetype='text/plain', template_name='books/author_list.txt' ) response["Content-Disposition"]="attachment;filename=authors.txt" returnresponse
这个方法之所以工作是因为通用视图返回的HttpResponse对象可以象一个字典一样的设置HTTP的头部。随便说一下,这个Content-Disposition的含义是告诉浏览器下载并保存这个页面,而不是在浏览器中显示它。