基于类的通用视图(Class-based generic views)

from:http://www.cnblogs.com/huafeng/p/3273315.html

在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.py
from  django.db  import  models
 
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()
 
     class  Meta:
         ordering  =  [ "-name" ]
 
     def  __unicode__( self ):
         return  self .name
 
class  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 .name
 
class  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.py
from  django.views.generic  import  ListView
from  books.models  import  Publisher
 
class  PublisherList(ListView):
     model  =  Publisher

然后将它加到urls中:

1
2
3
4
5
6
7
# urls.py
from  django.conf.urls  import  patterns, url
from  books.views  import  PublisherList
 
urlpatterns  =  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.py
from  django.views.generic  import  ListView
from  books.models  import  Publisher
 
class  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  DetailView
from  books.models  import  Publisher, Book
 
class  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  DetailView
from  books.models  import  Publisher, Book
 
class  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  ListView
from  books.models  import  Book
 
class  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  ListView
from  books.models  import  Book
 
class  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.py
from  books.views  import  PublisherBookList
 
urlpatterns  =  patterns('',
     (r '^books/([\w-]+)/$' , PublisherBookList.as_view()),
)

然后我们来写PublisherBookList视图:

1
2
3
4
5
6
7
8
9
10
11
12
# views.py
from  django.shortcuts  import  get_object_or_404
from  django.views.generic  import  ListView
from  books.models  import  Book, Publisher
 
class  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.py
 
class  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  AuthorDetailView
 
urlpatterns  =  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  DetailView
from  django.shortcuts  import  get_object_or_404
from  django.utils  import  timezone
from  books.models  import  Author
 
class  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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值