在Django1.3之前,通用视图是以函数的方式来实现的。基于函数的实现已经不 建议使用,建议使用这里介绍的基于类的实现方式。
基于类的通用视图(以及任何继承了Django提供的基础类的基于类的视图)都能够以下面两种方式被配置:子类化,或者直接通过URLconf来传递参数。
当你子类化一个类视图时,你可以重写一些属性(比如template_name)或者 一些方法(比如 get_context_data)在你的子类中来提供一些新的值或者方 法。考虑一下,比如,一个仅仅需要展示一个模板的视图,about.html。Django有一个通用视图来完成这个功能 - TemplateView - 因此你可以子类化它,然后重写模板的名称:
# some_app/views.py from django.views.generic import TemplateView class AboutView(TemplateView): template_name = "about.html"
这时,你只需要添加这个新的视图到你的URLconf配置中。因为类视图本身是一个类,我 们把URL指向 as_view 这个类方法来替代类本身,这是类视图的入口点:
# urls.py from django.conf.urls import patterns, url, include from some_app.views import AboutView urlpatterns = patterns('', (r'^about/', AboutView.as_view()),)
作为一个选择,如果你仅仅修改类视图中少量简单的属性,你可以直接传递新的属性 到类本身调用 as_view 方法中:
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")),)
一个类似的重写模式可以用在 RedirectView 的 url 属性上,这是另外一个简单的通用视图。
如果要深入理解class-based view, 那首先就要了解什么是Mixin! Django把基本的http请求和响应抽象出来, 封装成各自的类, 在使用过程中只需把各个基类聚合到一起使用, 并按照自己的要求重写自己需要的方法就可以了, 那么就把这些基类叫Mixin吧. 在Django中比较基础的Mixin主要有几类:
View(视图基础类)
SingleObjectMixin(单对象类)
MultipleObjectMixin(多对象类)
TemplateResponseMixin(模板响应类)
FormMixin(表单类)
YearMixin, MonthMixin, DayMixin, WeekMixin, DateMixin(几个基于时间关系的类)
其他的所有内置class-based view都是把以上几个基础类组合, 重写方法以达到预期的结果. 比如DetailView这个类就组合了SingleObjectTemplateResponseMixin和BaseDetailView.
TemplateView确实很有用,但是你可能需要把数据库中的内容查询展示出来,没关系,Django同样提供了大把内置的通用视图。下面看一个简单例子:
# 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.nameclass Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField('Author') publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
为了给所有的Publisher建立一个列表页,我们将按照这样的方式来配置URLconf:
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,template_name=’publisher_list.html‘, )),)
下面是简单的模板实例:
{% extends "base.html" %} {% block content %} <h2>Publishers</h2> <ul> {% for publisher in object_list %} <li>{{ publisher.name }}</li> {% endfor %} </ul> {% endblock %}
使用通用视图可以极大的提高开发速度。然而,在大多时候我们会发现通用视图无法满足需求。为此,我们可以对通用视图进行扩展来增加自己的功能。扩展通用视图的方法是子类化它们,并且重写它们的属性或者方法。
默认情况下,通用视图将所有相关Model的查询到的对象放到object_list变量中,这虽然能正常工作,但是对模板设计者不友好。
通用视图中的这个属性 : context_object_name指定上下文(context)变量要使用的名字。在这个例子中我们在URLconf中重写了它,因为这只是简单的修改:
urlpatterns = patterns('', (r'^publishers/$', ListView.as_view( model=Publisher, template_name=”publisher_list.html“, context_object_name="publisher_list", )),)
我们应该使用context_object_name来指定上下文(context)变量。
我们可能需要一些通用视图没有提供的额外信息,我们可以子类化DetailView然后提供你自己的 get_context_data方法的实现。
DetailView 中默认的实现只是简单的 给模板添加了要展示的对象,但是你这可以这么重写来展示更多信息:
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参数指定了视图(view)在哪个数据库模型之上进行操作,但是这个太不灵活了,我们可以使用 queryset参数来指定一个对象列表:
from django.views.generic import DetailView from books.models import Publisher, Book class PublisherDetailView(DetailView): context_object_name = "publisher" queryset = Publisher.objects.all()
指明model = Publisher 等价于快速声明的queryset = Publisher.objects.all() 。然而,使用 queryset可以定义一个过滤的对象列表:
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中的关键字来过滤对象。ListView 有一个 get_queryset() 方法来供我们重写。
这里,我们有一个URLconf定义了一组供捕获的参数:
from books.views import PublisherBookListView urlpatterns = patterns('', (r'^books/(\w+)/$', PublisherBookListView.as_view()),)
下一个,我们定义了 PublisherBookListView 视图:
from django.shortcuts import get_object_or_404 from django.views.generic import ListView from books.models import Book, Publisher class PublisherBookListView(ListView): context_object_name = "book_list" template_name = "books/books_by_publisher.html" def get_queryset(self): publisher = get_object_or_404(Publisher, name__iexact=self.args[0]) return Book.objects.filter(publisher=publisher)
http://django-14-tkliuxing.readthedocs.org/en/latest/topics/class-based-views.html
http://simple-is-better.com/news/643
http://simple-is-better.com/news/644