在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

​

转载请注明出处

基于类的通用视图(Class-based generic views)的更多相关文章

  1. 用基于类的通用视图处理表单(Class-based generic views)

    处理表单通常包含3步: 初始化GET(空白的后者预填充的表单) POST非法数据(通常重新显示带有错误信息的表单) POST合法数据(提交数据并重定向) 为了将你从这些烦人的重复步骤中解救出来,Dja ...

  2. Django 1.6 基于类的通用视图

    Django 1.6 基于类的通用视图 最初 django 的视图都是用函数实现的,后来开发出一些通用视图函数,以取代某些常见的重复性代码.通用视图就像是一些封装好的处理器,使用它们的时候只须要给出特 ...

  3. Django 基于类的通用视图

    在早期,我们认识到在视图开发过程中有共同的用法和模式.这时我们引入基于函数的通用视图来抽象这些模式以简化常见情形的视图开发. 基于函数视图的用法有以下三种: def index(request): r ...

  4. Django——django1.6 基于类的通用视图

    最初 django 的视图都是用函数实现的,后来开发出一些通用视图函数,以取代某些常见的重复性代码.通用视图就像是一些封装好的处理器,使用它们的时候只须要给出特定的参数集即可,不必关心具体的实现.各种 ...

  5. 介绍——基于类的视图(class-based view)

    ​刚开始的时候,django只有基于函数的视图(Function-based views).为了解决开发视图中繁杂的重复代码,基于函数的通用视图( Class-based generic views) ...

  6. Django——基于类的视图(class-based view)

    刚开始的时候,django只有基于函数的视图(Function-based views).为了解决开发视图中繁杂的重复代码,基于函数的通用视图( Funcation-based generic vie ...

  7. django通用视图(类方法)

    这周是我入职的第一周,入职第一天看到嘉兴大佬的项目代码.视图中有类方法,我感到很困惑. 联想到之前北京融360的电话面试,问我有无写过类方法……看来有必要了解下视图的类方法,上网搜了很多,原来这就是所 ...

  8. Django入门与实践-第22章:基于类的视图

    http://127.0.0.1:8000/boards/1/topics/2/posts/2/edit/ http://127.0.0.1:8000/ #boards/views.py from d ...

  9. Django——基于类的视图源码分析 一

    基于类的视图(Class-based view)是Django 1.3引入的新的视图编写方式,用于取代以前基于函数(Function-based)方式. 借助于OO和Python中方便的多重继承特性, ...

随机推荐

  1. springMVC集成logback日志系统

    一.项目结构 项目介绍:maven搭建的web项目,实现Java日志记录功能.其中logback.xml为日志配置文件,spring-mvc-servlet.xml为spring controller ...

  2. RabbitMQ (1) 环境安装

    1.下载erlang, 设置系统的环境变量 下载地址:http://www.erlang.org/downloads ERLANG_HOME=D:\Program\erl9.3 Path = %ERL ...

  3. Oracle 11g R2在 win7 64位的安装流程图解【含常见问题解决方案】

    ORACLE数据库系统是美国ORACLE公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/S体系结构的数据库之一.Oracle 11 ...

  4. CI 框架源码解析一之入口文件 index.php

    Index.php作为CI框架的入口文件,源码分析,自然而然由此开始.在源码分析的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现,如果英文水平很好的话,读过index.php文件的英文注释之 ...

  5. 操作 Java 数组的 12 个最佳方法

    1.  声明一个数组 Java代码: String[] aArray = new String[5]; String[] bArray = {"a","b",& ...

  6. python学习之路1(基本语法元素)

    1.变量与简单数据类型 1.1变量 变量就是给你所写代码的信息起一个名字,用来存储此信息,使信息变得更加的简洁易读, 例如:message = "Hello World!",其中m ...

  7. 27-Middleware管道介绍

    1-Middleware管道介绍,. 如果匹配上/task,则界面只会显示i am task. public void Configure(IApplicationBuilder app, IHost ...

  8. Android面试收集录14 Android进程间通信方式

    一.使用 Intent Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间 ...

  9. 17,saltstack高效运维

      salt介绍 saltstack是由thomas Hatch于2011年创建的一个开源项目,设计初衷是为了实现一个快速的远程执行系统. salt强大吗 系统管理员日常会进行大量的重复性操作,例如安 ...

  10. SpringMVC---四大注解

    SpringMVC四大注解 Component 通用标注,在不清楚使用哪个注解的时候,可以使用Component通用注解 Controller 标注web请求控制器 Service 标注Service ...