基于类的通用视图
前面我们说过了django的通用视图,不知道django通用视图的去看我前面的随笔,谢谢
django的通用视图帮我们省略了很多代码,但有时候django的通用视图并不能满足我们全部的需求,例如像重定义一些属性和方法的时候,或者我们想换种写法的时候,因此,django提供了基于类的通用视图,通过子类或者在url中传参的方法来配置我们的基于类的通用视图
通用视图和基于类的通用视图是两种不一样的写法,前面我们介绍的通用视图,所有的代码集中于url的配置文件中,而基于类的通用视图主要集中于配置“继承然后覆盖父类方法和属性”方面,下面是一个基于函数的视图和基于类的视图的对应关系
基于函数 | 基于类 |
---|---|
django.views.generic.simple.direct_to_template | django.views.generic.base.TemplateView |
django.views.generic.simple.redirect_to | django.views.generic.base.RedirectView |
django.views.generic.list_detail.object_list | django.views.generic.list.ListView |
django.views.generic.list_detail.object_detail | django.views.generic.detail.DetailView |
django.views.generic.create_update.create_object | django.views.generic.edit.CreateView |
django.views.generic.create_update.update_object | django.views.generic.edit.UpdateView |
django.views.generic.create_update.delete_object | django.views.generic.edit.DeleteView |
django.views.generic.date_based.archive_index | django.views.generic.dates.ArchiveIndexView |
django.views.generic.date_based.archive_year | django.views.generic.dates.YearArchiveView |
django.views.generic.date_based.archive_month | django.views.generic.dates.MonthArchiveView |
django.views.generic.date_based.archive_week | django.views.generic.dates.WeekArchiveView |
django.views.generic.date_based.archive_day | django.views.generic.dates.DayArchiveView |
django.views.generic.date_based.archive_today | django.views.generic.dates.TodayArchiveView |
django.views.generic.date_based.object_detail | django.views.generic.dates.DateDetailView |
简单用法
考虑你仅仅想要显示一个模板about.html,django提供了一个通用视图去做这件事,我们只需要继承它,然后覆盖覆盖模板名称即可,其实TemplateView类对应的是direct_to_template
# some_app/views.py from django.views.generic import TemplateView class AboutView(TemplateView): template_name = "about.html"
然后再url配置文件中直接调用相应的方法即可
# urls.py from django.conf.urls import patterns, url, include from some_app.views import AboutView urlpatterns = patterns('', (r'^about/', AboutView.as_view()), )
或者我们可以直接在url中通过传参的方法达到修改属性的目的
from django.conf.urls import patterns, url, include from django.views.generic import TemplateView urlpatterns = patterns('', (r'^about/', TemplateView.as_view(template_name="about.html")), )
对象的通用视图
首先这是我们可能会用到的model
# 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 Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
为了生成一个所有出版商的页面,我们可以在url中这样写
from django.conf.urls import patterns, url, include from django.views.generic import ListView from books.models import Publisher urlpatterns = patterns('', (r'^publishers/$', ListView.as_view( model=Publisher, )), )
在这里我们没有指定模板名,那么会使用ListView默认的模板名默认模板<app_label>/<model_name>_list.html ,这里可能是/books/publisher_list.html
拓展通用视图
制作友好的模板上下文名称
如果你在和模型打交道,使用下面的配置可以使你自己定义一个更好用(django内部有规则生成一个)的模板上下文名称,这可能使得和你合作的模板设计师对你有好感,因为有时候django自动生成的模板上下文名称很冗长
urlpatterns = patterns('', (r'^publishers/$', ListView.as_view( model=Publisher, context_object_name="publisher_list", )), )
增加额外的上下文内容
有时候默认的通用视图提供的上下文内容不能满足你的需求,比如一个返回所有出版商的列表的页面,上面可能一点和书籍的信息都没有,这时候你可以通过继承相应的父类,覆盖父类的方法为子类添加额外的内容
from django.views.generic import DetailView from books.models import Publisher, Book class PublisherDetailView(DetailView): context_object_name = "publisher" model = Publisher def get_context_data(self, **kwargs): # Call the base implementation first to get a context context = super(PublisherDetailView, self).get_context_data(**kwargs) # Add in a QuerySet of all the books context['book_list'] = Book.objects.all() return context
对象的查看子集
注意到的一点,model属性和queryset属性,model=Publisher和queryset=Publisher.objects.all()基本是一致的,基于这一点,我们可以通过制定queryset的方法以至于不必每次都收返回一个模型的全集,可以每次只返回一个子集,当然,我们也可以在url中达到这个目标
urlpatterns = patterns('', (r'^publishers/$', ListView.as_view( queryset=Publisher.objects.all(), context_object_name="publisher_list", )), (r'^books/$', ListView.as_view( queryset=Book.objects.order_by("-publication_date"), context_object_name="book_list", )), )
from django.views.generic import ListView from books.models import Book class AcmeBookListView(ListView): context_object_name = "book_list" queryset = Book.objects.filter(publisher__name="Acme Publishing") template_name = "books/acme_list.html"
动态筛选
还有一个常用的需求是根据url中的参数去查询数据库,之前我们可以把出版商的名字硬编码在url中,但如果我们要写一个能够根据任意给定的出版商,返回该出版商的所有书籍的视图呢?我们可以覆盖get_queryset()这个方法
def get_queryset(self): publisher = get_object_or_404(Publisher, name__iexact=self.args[0]) return Book.objects.filter(publisher=publisher)
执行额外的工作
假如Author这个模型有一个last_accessed的date类型属性,我们要求每次访问的时候都更新这个属性,显然,django的通用视图是根本不知道Author有这样的一个属性的,所以我们必须重写某个方法以增加这个行为,在这里是get_object()方法
import datetime from books.models import Author from django.views.generic import DetailView from django.shortcuts import get_object_or_404 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 = datetime.datetime.now() object.save() # Return the object return object
装饰基于类的视图
在url中装饰
from django.contrib.auth.decorators import login_required, permission_required from django.views.generic import TemplateView from .views import VoteView urlpatterns = patterns('', (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))), (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())), )
在类中装饰
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views.generic import TemplateView class ProtectedView(TemplateView): template_name = 'secret.html' @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ProtectedView, self).dispatch(*args, **kwargs)
p