The Django Book

SmNHmB <a href=”http://ynplfxmsfjas.com/“>ynplfxmsfjas</a>, [url=http://afzikbuhbzzy.com/]afzikbuhbzzy[/url], [link=http://rmvxarlrgmyq.com/]rmvxarlrgmyq[/link], http://lxjsvkwlvktf.com/

oMhafb <a href=”http://wxqvnkowipsh.com/“>wxqvnkowipsh</a>, [url=http://jgrcakhpncly.com/]jgrcakhpncly[/url], [link=http://rmojkmnglmff.com/]rmojkmnglmff[/link], http://npmxesmysuxb.com/

ESWfz2 <a href=”http://qhhwejzlvhdg.com/“>qhhwejzlvhdg</a>, [url=http://dgjhcvixtkbj.com/]dgjhcvixtkbj[/url], [link=http://zzqibhslmuii.com/]zzqibhslmuii[/link], http://snkoxhyeojqh.com/

2fPTAc <a href=”http://llijnkwkoyyd.com/“>llijnkwkoyyd</a>, [url=http://xxyyyrvmmlqq.com/]xxyyyrvmmlqq[/url], [link=http://dunaskbxxsll.com/]dunaskbxxsll[/link], http://olwmgfgxzaof.com/

PaYeM0 <a href=”http://dhmykebhsimg.com/“>dhmykebhsimg</a>, [url=http://hyizkgihaqdi.com/]hyizkgihaqdi[/url], [link=http://jcinmepstroe.com/]jcinmepstroe[/link], http://svbiputphxel.com/

  • 完成常用的简单任务:重定向到另一个页面以及渲染一个指定的模板。

38fEhz <a href=”http://cajhjkeswyws.com/“>cajhjkeswyws</a>, [url=http://bcggwqooqnsq.com/]bcggwqooqnsq[/url], [link=http://ittkllncleip.com/]ittkllncleip[/link], http://zfhojpnwcklx.com/

  • 呈现基于日期的数据的年/月/日归档页面,关联的详情页面,最新页面。Django Weblogs (http://www.djangoproject.com/weblog/)的年、月、日的归档就是使用通用视图 架构的,就像是典型的新闻报纸归档。

Pnx4JT <a href=”http://uokrhwbsdkgc.com/“>uokrhwbsdkgc</a>, [url=http://qbsfadvarxud.com/]qbsfadvarxud[/url], [link=http://gwdbaeqxnhlf.com/]gwdbaeqxnhlf[/link], http://nxukqcjxxtly.com/

i8AMSk <a href=”http://svhztxsiryev.com/“>svhztxsiryev</a>, [url=http://xhznnmbnuzku.com/]xhznnmbnuzku[/url], [link=http://gqqhpwyksfhs.com/]gqqhpwyksfhs[/link], http://rcpuvnqhetvh.com/

cSp6KO <a href=”http://zjtnkayajtcn.com/“>zjtnkayajtcn</a>, [url=http://skkxesnzsack.com/]skkxesnzsack[/url], [link=http://sakmtrjlashw.com/]sakmtrjlashw[/link], http://fkctfgdjzsjl.com/

EWMqsf <a href=”http://zqlypjgfuqno.com/“>zqlypjgfuqno</a>, [url=http://wlgybeuamsyg.com/]wlgybeuamsyg[/url], [link=http://pavtyxnjmwtt.com/]pavtyxnjmwtt[/link], http://rmdzoqinkave.com/

例如,下面是一个呈现静态“关于”页面的URLconf:

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template

urlpatterns = patterns('',
    ('^about/$', direct_to_template, {
        'template': 'about.html'
    })
)

一眼看上去似乎有点不可思议,不需要编写代码的视图!它和第八章中的例子完全一样: direct_to_template 视图从参数中获取渲染视图所需的相关信息。

因为通用视图都是标准的视图函数,我们可以在我们自己的视图中重用它。例如,我们扩展 about例子把映射的URL从 /about/<whatever>/ 到一个静态渲染 about/<whatever>.html 。 我们首先修改URL配置到新的视图函数:

from django.conf.urls.defaults import *
from django.views.generic.simple import direct_to_template
**from mysite.books.views import about_pages**

urlpatterns = patterns('',
    ('^about/$', direct_to_template, {
        'template': 'about.html'
    }),
    **('^about/(w+)/$', about_pages),**
)

接下来,我们编写 about_pages 视图的代码:

from django.http import Http404
from django.template import TemplateDoesNotExist
from django.views.generic.simple import direct_to_template

def about_pages(request, page):
    try:
        return direct_to_template(request, template="about/%s.html" % page)
    except TemplateDoesNotExist:
        raise Http404()

在这里我们象使用其他函数一样使用 direct_to_template 。因为它返回一个 HttpResponse 对象,我们只需要简单的返回它就好了。有一个稍微复杂的地方,要处理没有找到模板的情况。 我们不希望一个不存在的模板引发服务器错误,所以我们捕捉 TempalteDoesNotExist 异常 并返回404错误。

这里有没有安全性问题?

眼尖的读者可能已经注意到一个可能的安全漏洞:我们直接使用从客户端浏览器来的数据构造 模板名称(template="about/%s.html" % page )。乍看起来,这像是一个经典的 目录遍历(directory traversal) 攻击(详情请看第十九章)。事实真是这样吗?

完全不是。是的,一个恶意的 page 值可以导致目录跨越,但是尽管 page 从 请求的URL中获取的,并不是所有的值都被接受。这就是URL配置的关键所在:我们使用正则表达式 \w+ 来从URL里匹配 page ,而 \w 只接受字符和数字。因此,任何恶意的字符 (例如在这里是点 . 和正斜线 / )将在URL解析时被拒绝,根本不会传递给视图函数。

对象的通用视图

direct_to_template 毫无疑问是非常有用的,但Django通用视图最有用的是在呈现 数据库中的数据。因为这个应用实在太普遍了,Django带有很多内建的通用视图来帮助你很容易 的生成对象的列表和明细视图。

让我们先看看其中的一个通用视图:对象列表视图。我们使用第五章中的 Publisher 来举例:

class Publisher(models.Model):
    name = models.CharField(maxlength=30)
    address = models.CharField(maxlength=50)
    city = models.CharField(maxlength=60)
    state_province = models.CharField(maxlength=30)
    country = models.CharField(maxlength=50)
    website = models.URLField()

    def __str__(self):
        return self.name

    class Meta:
        ordering = ["-name"]

    class Admin:
        pass

要为所有的书籍创建一个列表页面,我们使用下面的URL配置:

from django.conf.urls.defaults import *
from django.views.generic import list_detail
from mysite.books.models import Publisher

publisher_info = {
    "queryset" : Publisher.objects.all(),
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

这就是所要编写的所有Python代码。当然,我们还需要编写一个模板。我们可以通过在额外参数 字典里包含 template_name 来清楚的告诉 object_list 视图使用哪个模板,但是 由于Django在不给定模板的时候会用对象的名称推导出一个。在这个例子中,这个推导出的模板名称 将是 "books/publisher_list.html" ,其中books部分是定义这个模型的app的名称, publisher部分是这个模型名称的小写。

这个模板将按照 context 中包含的变量 object_list 来渲染,这个变量包含所有的书籍对象。 一个非常简单的模板看起来象下面这样:

{% extends "base.html" %}

{% block content %}
    <h2>Publishers</h2>
    <ul>
        {% for publisher in object_list %}
            <li>{{ publisher.name }}</li>
        {% endfor %}
    </ul>
{% endblock %}

这就是所有要做的事。要使用通用视图酷酷的特性只需要修改参数字典并传递给通用视图函数。 附录D是通用视图的完全参考资料;本章接下来的章节将讲到自定义和扩展通用视图的一些方法。

扩展通用视图

毫无疑问,使用通用视图可以充分加快开发速度。然而,在多数的工程中,也会出现通用视图不能 满足需求的情况。实际上,刚接触Django的开发者最常见的问题就是怎样使用通用视图来处理更多的情况。

DNJEsI <a href=”http://xjeubrblziif.com/“>xjeubrblziif</a>, [url=http://dobvanweoamc.com/]dobvanweoamc[/url], [link=http://djyiudlubona.com/]djyiudlubona[/link], http://lixowftpfyrv.com/

制作友好的模板Context

GopzWR <a href=”http://wyzfvmcuticl.com/“>wyzfvmcuticl</a>, [url=http://jxzqmqdmwetz.com/]jxzqmqdmwetz[/url], [link=http://piilbiaihfax.com/]piilbiaihfax[/link], http://htyxquhhvexk.com/

8Vyjss <a href=”http://rhhhrvhkabeq.com/“>rhhhrvhkabeq</a>, [url=http://ahvmqfezgjmk.com/]ahvmqfezgjmk[/url], [link=http://smovmqdvshdx.com/]smovmqdvshdx[/link], http://vibqzvjswygw.com/

publisher_info = {
    "queryset" : Publisher.objects.all(),
    **"template_object_name" : "publisher",**
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info)
)

使用有用的 template_object_name 总是个好想法。你的设计模板的合作伙伴会感谢你的。

添加额外的Context

你常常需要呈现比通用视图提供的更多的额外信息。例如,考虑一下在每个出版商页面实现所有其他 出版商列表。 object_detail 通用视图提供了出版商到context,但是看起来没有办法在模板中 获取 所有 出版商列表。

这是解决方法:所有的通用视图都有一个额外的可选参数 extra_context 。这个参数是一个字典数据类型,包含要添加到模板的context中的额外的对象。所以要提供所有的出版商明细给视图,我们就用这样的info字典:

publisher_info = {
    "queryset" : Publisher.objects.all(),
    "template_object_name" : "publisher",
    **"extra_context" : {"book_list" : Book.objects.all()}**
}

这样就把一个 {{ book_list }} 变量放到模板的context中。这个方法可以用来传递任意数据 到通用视图模板中去,非常方便。

11BuSm <a href=”http://eqbfztxatswg.com/“>eqbfztxatswg</a>, [url=http://cqyvbzdkmltn.com/]cqyvbzdkmltn[/url], [link=http://crybwpchprla.com/]crybwpchprla[/link], http://zsruvjbcerqm.com/

我们现在来看一下, extra_context 里包含数据库查询的问题。因为在这个例子中,我们把 Publisher.objects.all() 放在URLconf中,它只会执行一次(当URLconf第一次加载的时候)。当你添加或删除出版商,你会发现在重启Web服务器之前,通用视图不会反映出这些修改的(有关QuerySet何时被缓存和赋值的更多信息请参考附录C中“缓存与查询集”一节)。

备注

PHysZ7 <a href=”http://jbnqwfshbxue.com/“>jbnqwfshbxue</a>, [url=http://igfjyorkpypm.com/]igfjyorkpypm[/url], [link=http://wkdyrkxwmdfo.com/]wkdyrkxwmdfo[/link], http://wjhpynkhkixt.com/

解决这个问题的办法是在 extra_context 中用一个回调(callback)来 代替使用一个变量。任何可以调用的对象(例如一个函数)在传递给 extra_context 后都会在每次视图渲染前执行(而不是只执行一次)。 你可以象这样定义一个函数:

def get_books():
    return Book.objects.all()

publisher_info = {
    "queryset" : Publisher.objects.all(),
    "template_object_name" : "publisher",
    "extra_context" : **{"book_list" : get_books}**
}

O2zMLs <a href=”http://imijpqyscyzu.com/“>imijpqyscyzu</a>, [url=http://isrvqdjbewfq.com/]isrvqdjbewfq[/url], [link=http://tiwdtcivjwov.com/]tiwdtcivjwov[/link], http://wlgvlcjtvply.com/

publisher_info = {
    "queryset" : Publisher.objects.all(),
    "template_object_name" : "publisher",
    "extra_context" : **{"book_list" : Book.objects.all}**
}

np4DMd <a href=”http://hbsnjcfoizvz.com/“>hbsnjcfoizvz</a>, [url=http://wdikbtkrqjrv.com/]wdikbtkrqjrv[/url], [link=http://hrfbyhalrzta.com/]hrfbyhalrzta[/link], http://joxjwznpivhb.com/

8LlhZj <a href=”http://wzekxqnfpltm.com/“>wzekxqnfpltm</a>, [url=http://nfqbyffbfkil.com/]nfqbyffbfkil[/url], [link=http://byygkbksdadj.com/]byygkbksdadj[/link], http://xbrpupjsjpvs.com/

现在让我们来仔细看看这个 queryset 。大多数通用视图有一个 queryset 参数,这个参数告诉视图要显示对象的集合 (有关QuerySet的解释请看第五章的 “选择对象”章节,详细资料请参看附录C)。

bQLEb6 <a href=”http://smcfsnorksba.com/“>smcfsnorksba</a>, [url=http://aruhrbpgpnzq.com/]aruhrbpgpnzq[/url], [link=http://poapiucigzvo.com/]poapiucigzvo[/link], http://yqctgzvjhqfl.com/

book_info = {
    "queryset" : Book.objects.all().order_by("-publication_date"),
}

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    **(r'^books/$', list_detail.object_list, book_info),**
)

这是一个相当简单的例子,但是很说明问题。当然,你通常还想做比重新排序更多的事。 如果你想要呈现某个特定出版商出版的所有书籍列表,你可以使用同样的技术:

**apress_books = {**
    **"queryset": Book.objects.filter(publisher__name="Apress Publishing"),**
    **"template_name" : "books/apress_list.html"**
**}**

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    **(r'^books/apress/$', list_detail.object_list, apress_books),**
)

注意 在使用一个过滤的 queryset 的同时,我们还使用一个自定义的模板名称。 如果我们不这么做,通用视图就会用以前的模板,这可能不是我们想要的结果。

MM4VfH <a href=”http://xkukcvuspnqq.com/“>xkukcvuspnqq</a>, [url=http://hajkdanceogm.com/]hajkdanceogm[/url], [link=http://rqjewsqkkmjt.com/]rqjewsqkkmjt[/link], http://uhfoivgnfeiu.com/

备注

如果你在请求 /books/apress/ 时出现404错误,请检查以确保你的数据库中出版商 中有名为Apress Publishing的记录。通用视图有一个 allow_empty 参数可以 用来处理这个情况,详情请看附录D。

用函数包装来处理复杂的数据过滤

yKnZxQ <a href=”http://bpzdrpbbjsjf.com/“>bpzdrpbbjsjf</a>, [url=http://cmholoypoxsv.com/]cmholoypoxsv[/url], [link=http://ihoiztkugwlg.com/]ihoiztkugwlg[/link], http://ozigifixmcgl.com/

urlpatterns = patterns('',
    (r'^publishers/$', list_detail.object_list, publisher_info),
    **(r'^books/(w+)/$', books_by_publisher),**
)

接下来,我们写 books_by_publisher 这个视图:(上面的代码中正则表达式有误,在 w 前要加反斜線)

from django.http import Http404
from django.views.generic import list_detail
from mysite.books.models import Book, Publisher

def books_by_publisher(request, name):

    # Look up the publisher (and raise a 404 if it can't be found).
    try:
        publisher = Publisher.objects.get(name__iexact=name)
    except Publisher.DoesNotExist:
        raise Http404

    # Use the object_list view for the heavy lifting.
    return list_detail.object_list(
        request,
        queryset = Book.objects.filter(publisher=publisher),
        template_name = "books/books_by_publisher.html",
        template_object_name = "books",
        extra_context = {"publisher" : publisher}
    )

这是因为通用视图就是Python函数。和其他的视图函数一样,通用视图也是接受一些 参数并返回 HttpResponse 对象。因此,通过包装通用视图函数可以做更多的事。

注意

XGFs79 <a href=”http://orvtfnnzdtpg.com/“>orvtfnnzdtpg</a>, [url=http://bowbdcdfhptd.com/]bowbdcdfhptd[/url], [link=http://jjgdzbrfozjj.com/]jjgdzbrfozjj[/link], http://gnqujsbgtowe.com/

处理额外工作

我们再来看看最后一个常用模式:在调用通用视图前后做些额外工作。

想象一下我们在 Author 对象里有一个 last_accessed 字段,我们用这个字段来更正对author的最近访问时间。当然通用视图 object_detail 并不能处理 这个问题,我们可以很容易的写一个自定义的视图来更新这个字段。

首先,我们需要在URL配置里设置指向到新的自定义视图:

from mysite.books.views import author_detail

urlpatterns = patterns('',
    #...
    **(r'^authors/(?P<author_id>d+)/$', author_detail),**
)

接下来写包装函数:

import datetime
from mysite.books.models import Author
from django.views.generic import list_detail
from django.shortcuts import get_object_or_404

def author_detail(request, author_id):
    # Look up the Author (and raise a 404 if she's not found)
    author = get_object_or_404(Author, pk=author_id)

    # Record the last accessed date
    author.last_accessed = datetime.datetime.now()
    author.save()

    # Show the detail page
    return list_detail.object_detail(
        request,
        queryset = Author.objects.all(),
        object_id = author_id,
    )

注意

除非你添加 last_accessed 字段到你的 Author 模型并创建 books/author_detail.html 模板,否则这段代码不能真正工作。

我们可以用同样的方法修改通用视图的返回值。如果我们想要提供一个供下载用的 纯文本版本的author列表,我们可以用下面这个视图:

def author_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"
    return response

这个方法之所以工作是因为通用视图返回的 HttpResponse 对象可以象一个字典 一样的设置HTTP的头部。随便说一下,这个 Content-Disposition 的含义是 告诉浏览器下载并保存这个页面,而不是在浏览器中显示它。

下一步

在这一章我们只讲了Django带的通用视图其中一部分,不过这些方法也适用于其他的 通用视图。有关更详细的内容,请看附录D。

在下一章中我们将深入到Django模板系统的内部去,展示所有扩展它的酷方法。目前 为止,我们还只是把模板引擎当作一个渲染内容的静态工具。

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Chinese translate hosting by py3k.cn.