The Django Book

Chapter 11: Generating Non-HTML Content

第十一章 输出非HTML内容

Usually when we talk about developing Web sites, were talking about producing HTML. Of course, theres a lot more to the Web than HTML; we use the Web to distribute data in all sorts of formats: RSS, PDFs, images, and so forth.


So far weve focused on the common case of HTML production, but in this chapter well take a detour and look at using Django to produce other types of content.

到目前为止,我们的注意力都是放在常见 HTML 代码生成上,但是在这一章中,我们将会对使用 Django 生成其它格式的内容进行简要介绍。

Django has convenient built-in tools that you can use to produce some common non-HTML content:


  • RSS/Atom syndication feeds

  • RSS/Atom 聚合文件

  • Sitemaps (an XML format originally developed by Google that gives hints to search engines)

  • 站点地图 (一个XML格式文件,最初由Google开发,用于给搜索引擎提示线索)

Well examine each of those tools a little later on, but first well cover the basic principles.


The basics: views and MIME-types

基础: 视图和MIME类型

Remember this from Chapter 3?


A view function, or view for short, is simply a Python function that takes a Web request and returns a Web response. This response can be the HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an imageor anything, really.

一个视图函数(view function),或者简称 view ,只不过是一个可以处理一个Web请求并且返回 一个Web响应的Python函数。这个响应可以是一个Web页面的HTML内容,或者一个跳转,或者一个404 错误,或者一个XML文档,或者一幅图片,或者映射到任何东西上。

More formally, a Django view function must

更正式的说,一个Django视图函数 必须

  • Accept an HttpRequest instance as its first argument

  • 接受一个 HttpRequest 实例作为它的第一个参数

  • Return an HttpResponse instance

  • 返回一个 HttpResponse 实例

The key to returning non-HTML content from a view lies in the HttpResponse class, specifically the mimetype constructor argument. By tweaking the MIME type, we can indicate to the browser that weve returned a response of a different format.

从一个视图返回一个非 HTML 内容的关键是在构造一个 HttpResponse 类时,需要指定 mimetype 参数。通过改变 MIME 类型,我们可以告知浏览器将要返回的数据是另一种不同的类型。

For example, lets look at a view that returns a PNG image. To keep things simple, well just read the file off the disk:


from django.http import HttpResponse

def my_image(request):
    image_data = open("/path/to/my/image.png", "rb").read()
    return HttpResponse(image_data, mimetype="image/png")

Thats it! If you replace the image path in the open() call with a path to a real image, you can use this very simple view to serve an image, and the browser will display it correctly.

就是这么简单。如果改变 open() 中的图片路径为一张真实图片的路径,那么就可以使用这个十分简单的视图来提供一张图片,并且浏览器可以正确的显示它。

The other important thing to keep in mind is that HttpResponse objects implement Pythons standard file API. This means that you can use an HttpResponse instance in any place Python (or a third-party library) expects a file.


For an example of how that works, lets take a look at producing CSV with Django.

下面将用 Django 生成 CSV 文件为例,说明它的工作原理。

Producing CSV

生成 CSV 文件

CSV is a simple data format usually used by spreadsheet software. Its basically a series of table rows, with each cell in the row separated by a comma (CSV stands for comma-separated values ). For example, heres some data on unruly airline passengers in CSV format:

CSV 是一种简单的数据格式,通常为电子表格软件所使用。它主要是由一系列的表格行组成,每行中单元格之间使用逗号(CSV 是 逗号分隔数值(comma-separated values) 的缩写)隔开。例如,下面是以 CSV 格式记录的一些违规航班乘客的数据。

Year,Unruly Airline Passengers



The preceding listing contains real numbers; they come courtesy of the US Federal Aviation Administration. See


Though CSV looks simple, its not a format thats ever been formally defined. Different pieces of software produce and consume different variants of CSV, making it a bit tricky to use. Luckily, Python comes with a standard CSV library, csv , that is pretty much bulletproof.

虽然 CSV 看上去简单,以至于简单到这个格式甚至都没有正式的定义。但是不同的软件会生成和使用不同的 CSV 的变种,在使用上会有一些不便。幸运的是, Python 使用的是标准 CSV 库, csv ,所以它更通用。

Because the csv module operates on file-like objects, its a snap to use an HttpResponse instead:

因为 csv 模块操作的是类似文件的对象,所以可以使用 HttpResponse 替换:

import csv
from django.http import HttpResponse

# Number of unruly passengers each year 1995 - 2005. In a real application
# this would likely come from a database or some other back-end data store.
UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203]

def unruly_passengers_csv(request):
    # Create the HttpResponse object with the appropriate CSV header.
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename=unruly.csv'

    # Create the CSV writer using the HttpResponse as the "file"
    writer = csv.writer(response)
    writer.writerow(['Year', 'Unruly Airline Passengers'])
    for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS):
        writer.writerow([year, num])

    return response

The code and comments should be pretty clear, but a few things deserve special mention:


  • The response is given the text/csv MIME type (instead of the default text/html ). This tells browsers that the document is a CSV file.

  • 响应返回的是 text/csv MIME类型(而非默认的 text/html )。这会告诉浏览器,返回的文档是CSV文件。

  • The response gets an additional Content-Disposition header, which contains the name of the CSV file. This header (well, the attachment part) will instruct the browser to prompt for a location to save the file (instead of just displaying it). This file name is arbitrary; call it whatever you want. It will be used by browsers in the Save As dialog.

  • 响应会有一个附加的 Content-Disposition 头部,它包含有CSV文件的文件名。这个头部(或者说,附加部分)会指示浏览器弹出对话框询问文件存放的位置(而不仅仅是显示)。这个文件名是任意的,它会用在浏览器的另存为对话框中。

  • Hooking into the CSV-generation API is easy: just pass response as the first argument to csv.writer . The csv.writer function expects a filelike object, and HttpResponse objects fit the bill.

  • 与创建CSV的应用程序界面(API)挂接是很容易的:只需将 response 作为第一个变量传递给 csv.writercsv.writer 函数希望获得一个文件类的对象, HttpResponse 正好能达成这个目的。

  • For each row in your CSV file, call writer.writerow , passing it an iterable object such as a list or tuple.

  • 调用 writer.writerow ,并且传递给它一个类似 list 或者 tuple 的可迭代对象,就可以在 CSV 文件中写入一行。

  • The CSV module takes care of quoting for you, so you dont have to worry about escaping strings with quotes or commas in them. Just pass information to writerow() , and it will do the right thing.

  • CSV 模块考虑到了引用的问题,所以您不用担心逸出字符串中引号和逗号。只要把信息传递给 writerow() ,它会处理好所有的事情。

This is the general pattern youll use any time you need to return non-HTML content: create an HttpResponse response object (with a special MIME type), pass it to something expecting a file, and then return the response.

在任何需要返回非 HTML 内容的时候,都需要经过以下几步:创建一个 HttpResponse 响应对象(需要指定特殊的 MIME 类型)。将它作为参数传给一个需要文件的方法,然后返回这个响应。

Lets look at a few more examples.


Generating PDFs

生成 PDF 文件

Portable Document Format (PDF) is a format developed by Adobe thats used to represent printable documents, complete with pixel-perfect formatting, embedded fonts, and 2D vector graphics. You can think of a PDF document as the digital equivalent of a printed document; indeed, PDFs are usually used when you need to give a document to someone else to print.

便携文件格式 (PDF) 是由 Adobe 开发的格式,主要用于呈现可打印的文档,包含有 pixel-perfect 格式,嵌入字体以及2D矢量图像。PDF 文件可以被认为是一份打印文档的数字等价物;实际上,PDF 文件通常用于需要将文档交付给其他人去打印的场合。

You can easily generate PDFs with Python and Django thanks to the excellent open source ReportLab library ( The advantage of generating PDF files dynamically is that you can create customized PDFs for different purposes say, for different users or different pieces of content.

便携文件格式 (PDF) 是由 Adobe 开发的格式,主要用于呈现可打印的文档,包含有 pixel-perfect 格式,嵌入字体以及2D矢量图像。PDF 文件可以被认为是一份打印文档的数字等价物;实际上,PDF 文件通常用于需要将文档交付给其他人去打印的场合。

For example, we used Django and ReportLab at to generate customized, printer-ready NCAA tournament brackets.

下面的例子是使用 Django 和 ReportLab 在 上生成个性化的可打印的 NCAA 赛程表 (tournament brackets) 。

Installing ReportLab

安装 ReportLab

Before you do any PDF generation, however, youll need to install ReportLab. Its usually pretty simple: just download and install the library from

在生成 PDF 文件之前,需要安装 ReportLab 库。这通常是个很简单的过程:从 下载并且安装这个库即可。

The user guide (naturally available only as a PDF file) at has additional installation instructions.

使用手册(原始的只有 PDF 格式)可以从 下载,其中包含有一些其它的安装指南。



If youre using a modern Linux distribution, you might want to check your package management utility before installing ReportLab. Most package repositories have added ReportLab.

如果使用的是一些新的 Linux 发行版,则在安装前可以先检查包管理软件。多数软件包仓库中都加入了 ReportLab 。

For example, if youre using the (excellent) Ubuntu distribution, a simple apt-get install python-reportlab will do the trick nicely.

比如,如果使用(杰出的) Ubuntu 发行版,只需要简单的 apt-get install python-reportlab 一行命令即可完成安装。

Test your installation by importing it in the Python interactive interpreter:

在 Python 交互环境中导入这个软件包以检查安装是否成功。

>>> import reportlab

If that command doesnt raise any errors, the installation worked.


Writing Your View


Like CSV, generating PDFs dynamically with Django is easy because the ReportLab API acts on filelike objects.

和 CSV 类似,由 Django 动态生成 PDF 文件很简单,因为 ReportLab API 同样可以使用类似文件对象。

Heres a Hello World example:

下面是一个 Hello World 的示例:

from reportlab.pdfgen import canvas
from django.http import HttpResponse

def hello_pdf(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=hello.pdf'

    # Create the PDF object, using the response object as its "file."
    p = canvas.Canvas(response)

    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    p.drawString(100, 100, "Hello world.")

    # Close the PDF object cleanly, and we're done.
    return response

A few notes are in order:


  • Here we use the application/pdf MIME type. This tells browsers that the document is a PDF file, rather than an HTML file. If you leave off this information, browsers will probably interpret the response as HTML, which will result in scary gobbledygook in the browser window.

  • 这里我们使用的 MIME 类型是 application/pdf 。这会告诉浏览器这个文档是一个 PDF 文档,而不是 HTML 文档。如果忽略了这个参数,浏览器可能会把这个文件看成 HTML 文档,这会使浏览器的窗口中出现很奇怪的文字。

  • Hooking into the ReportLab API is easy: just pass response as the first argument to canvas.Canvas . The Canvas class expects a filelike object, and HttpResponse objects fit the bill.

  • 使用 ReportLab 的 API 很简单:只需要将 response 对象作为 canvas.Canvas 的第一个参数传入。 Canvas 类需要一个类似文件的对象, HttpResponse 对象可以满足这个要求。

  • All subsequent PDF-generation methods are called on the PDF object (in this case, p ), not on response .

  • 所有后续的 PDF 生成方法需要由 PDF 对象调用(在本例中是 p ),而不是 response 对象。

  • Finally, its important to call showPage() and save() on the PDF file (or else youll end up with a corrupted PDF file).

  • 最后需要对 PDF 文件调用 showPage()save() 方法(否则你会得到一个损坏的 PDF 文件)。

Complex PDFs

复杂的 PDF 文件

If youre creating a complex PDF document (or any large data blob), consider using the cStringIO library as a temporary holding place for your PDF file. The cStringIO library provides a file-like object interface that is written in C for maximum efficiency.

如果您在创建一个复杂的 PDF 文档(或者任何较大的数据块),请使用 cStringIO 库存放临时生成的 PDF 文件。 cStringIO 提供了一个用 C 编写的类似文件对象的接口,从而可以使系统的效率最高。

Heres the previous Hello World example rewritten to use cStringIO :

下面是使用 cStringIO 重写的 Hello World 例子:

from cStringIO import StringIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse

def hello_pdf(request):
    # Create the HttpResponse object with the appropriate PDF headers.
    response = HttpResponse(mimetype='application/pdf')
    response['Content-Disposition'] = 'attachment; filename=hello.pdf'

    temp = StringIO()

    # Create the PDF object, using the StringIO object as its "file."
    p = canvas.Canvas(temp)

    # Draw things on the PDF. Here's where the PDF generation happens.
    # See the ReportLab documentation for the full list of functionality.
    p.drawString(100, 100, "Hello world.")

    # Close the PDF object cleanly.

    # Get the value of the StringIO buffer and write it to the response.
    return response

Other Possibilities


Theres a whole host of other types of content you can generate in Python. Here are a few more ideas and some pointers to libraries you could use to implement them:

使用 Python 可以生成许多其它类型的内容,下面介绍的是一些其它的想法和一些可以用以实现它们的库。

ZIP files : Pythons standard library ships with the zipfile module, which can both read and write compressed ZIP files. You could use it to provide on-demand archives of a bunch of files, or perhaps compress large documents when requested. You could similarly produce TAR files using the standard library tarfile module.

ZIP 文件 :Python 标准库中包含有 zipfile 模块,它可以读和写压缩的 ZIP 文件。它可以用于按需生成一些文件的压缩包,或者在需要时压缩大的文档。如果是 TAR 文件则可以使用标准库 tarfile 模块。

Dynamic images : The Python Imaging Library (PIL; is a fantastic toolkit for producing images (PNG, JPEG, GIF, and a whole lot more). You could use it to automatically scale down images into thumbnails, composite multiple images into a single frame, or even do Web-based image processing.

动态图片 : Python 图片处理库 (PIL; 是极好的生成图片(PNG, JPEG, GIF 以及其它许多格式)的工具。它可以用于自动为图片生成缩略图,将多张图片压缩到单独的框架中,或者是做基于 Web 的图片处理。

Plots and charts : There are a number of incredibly powerful Python plotting and charting libraries you could use to produce on-demand maps, charts, plots, and graphs. We cant possibly list them all, so here are a couple of the highlights:

图表 : Python 有许多出色并且强大的图表库用以绘制图表,按需地图,表格等。我们不可能将它们全部列出,所以下面列出的是个中的翘楚。

In general, any Python library capable of writing to a file can be hooked into Django. The possibilities really are endless.

总之,所有可以写文件的库都可以与 Django 同时使用。请相信一切皆有可能。

Now that weve looked at the basics of generating non-HTML content, lets step up a level of abstraction. Django ships with some pretty nifty built-in tools for generating some common types of non-HTML content.


The Syndication Feed Framework


Django comes with a high-level syndication-feed-generating framework that makes creating RSS and Atom feeds easy.

Django带来了一个高级的聚合生成框架,它使得创建RSS和Atom feeds变得非常容易。

Whats RSS? Whats Atom?


RSS and Atom are both XML-based formats you can use to provide automatically updating feeds of your sites content. Read more about RSS at, and get information on Atom at

RSS和Atom都是基于XML的格式,你可以用它来提供有关你站点内容的自动更新的feed。了解更多关于RSS的可以访问, 更多Atom的信息可以访问

To create any syndication feed, all you have to do is write a short Python class. You can create as many feeds as you want.

想创建一个联合供稿的源(syndication feed),所需要做的只是写一个简短的python类。你可以创建任意多的源(feed)。

The high-level feed-generating framework is a view thats hooked to /feeds/ by convention. Django uses the remainder of the URL (everything after /feeds/ ) to determine which feed to return.

高级feed生成框架是一个默认绑定到/feeds/的视图,Django使用URL的其它部分(在/feeds/之后的任何东西)来决定输出 哪个feed

To create a feed, youll write a Feed class and point to it in your URLconf (see Chapters 3 and 8 for more about URLconfs).

要创建一个feed, 您将创建一个 Feed 类, 并在您的 URLconf 中指向它. ( 查看第3章和第8章, 可以获取更多有关URLconfs的更多信息 )



To activate syndication feeds on your Django site, add this URLconf:

为了在您的Django站点中激活syndication feeds, 添加如下的 URLconf:

 {'feed_dict': feeds}

This line tells Django to use the RSS framework to handle all URLs starting with "feeds/" . (You can change that "feeds/" prefix to fit your own needs.)

这一行告诉Django使用RSS框架处理所有的以 "feeds/" 开头的URL. ( 你可以修改 "feeds/" 前缀以满足您自己的要求. )

This URLconf line has an extra argument: {'feed_dict': feeds} . Use this extra argument to pass the syndication framework the feeds that should be published under that URL.

URLConf里有一行参数:``{‘feed_dict’: feeds}``,这个参数可以把对应URL需要发布的feed内容传递给 syndication framework

Specifically, feed_dict should be a dictionary that maps a feeds slug (short URL label) to its Feed class. You can define the feed_dict in the URLconf itself. Heres a full example URLconf:

特别的,feed_dict应该是一个映射feed的slug(简短URL标签)到它的Feed类的字典 你可以在URL配置本身里定义feed_dict,这里是一个完整的例子

from django.conf.urls.defaults import *
from myproject.feeds import LatestEntries, LatestEntriesByCategory

feeds = {
    'latest': LatestEntries,
    'categories': LatestEntriesByCategory,

urlpatterns = patterns('',
    # ...
    (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': feeds}),
    # ...

The preceding example registers two feeds:


  • The feed represented by LatestEntries will live at feeds/latest/ .

  • LatestEntries``表示的内容将对应到``feeds/latest/ .

  • The feed represented by LatestEntriesByCategory will live at feeds/categories/ .

  • LatestEntriesByCategory``的内容将对应到 ``feeds/categories/ .

Once thats set up, youll need to define the Feed classes themselves.

以上的设定完成之后,接下来需要自己定义 Feed

A Feed class is a simple Python class that represents a syndication feed. A feed can be simple (e.g., a site news feed, or a basic feed displaying the latest entries of a blog) or more complex (e.g., a feed displaying all the blog entries in a particular category, where the category is variable).

一个 Feed 类是一个简单的python类,用来表示一个syndication feed. 一个feed可能是简单的 (例如一个站点新闻feed,或者最基本的,显示一个blog的最新条目),也可能更加复杂(例如一个显示blog某一类别下所有条目的feed。这里类别 category 是个变量).

Feed classes must subclass django.contrib.syndication.feeds.Feed . They can live anywhere in your code tree.


A Simple Feed


This simple example, taken from, describes a feed of the latest five news items:


from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem

class LatestEntries(Feed):
    title = " site news"
    link = "/sitenews/"
    description = "Updates on changes and additions to"

    def items(self):
        return NewsItem.objects.order_by('-pub_date')[:5]

The important things to notice here are as follows:


The class subclasses django.contrib.syndication.feeds.Feed .

子类 django.contrib.syndication.feeds.Feed .

title , link , and description correspond to the standard RSS <title> , <link> , and <description> elements, respectively.

title , link , 和 description 对应一个标准 RSS 里的 <title> , <link> , 和 <description> 标签.

items() is simply a method that returns a list of objects that should be included in the feed as <item> elements. Although this example returns NewsItem objects using Djangos database API, items() doesnt have to return model instances.

items() 是一个方法,返回一个用以包含在包含在feed的 <item> 元素里的 list 虽然例子里用Djangos database API返回的 NewsItem 对象, items() 不一定必须返回 model的实例

You do get a few bits of functionality for free by using Django models, but items() can return any type of object you want.

你可以利用 Django models免费实现一定功能,但是 items() 可以返回你想要的任意类型的对象.

Theres just one more step. In an RSS feed, each <item> has a <title> , <link> , and <description> . We need to tell the framework what data to put into those elements.

还有一个步骤,在一个RSS feed里,每个(item)有一个(title),(link)和(description),我们需要告诉框架 把数据放到这些元素中

To specify the contents of <title> and <description> , create Django templates (see Chapter 4) called feeds/latest_title.html and feeds/latest_description.html , where latest is the slug specified in the URLconf for the given feed. Note that the .html extension is required.

如果要指定 <title><description> ,可以建立一个Django模板(见Chapter 4)名字叫 feeds/latest_title.htmlfeeds/latest_description.html ,后者是URLConf里为对应feed指定的 slug 。注意 .html 后缀是必须的。

The RSS system renders that template for each item, passing it two template context variables:


  • obj : The current object (one of whichever objects you returned in items() ).

  • obj : 当前对象 ( 返回到 items() 任意对象之一 )。

  • site : A django.models.core.sites.Site object representing the current site. This is useful for {{ site.domain }} or {{ }} .

  • site : 一个表示当前站点的 django.models.core.sites.Site 对象。 这对于 {{ site.domain }} 或者 {{ }} 很有用。

If you dont create a template for either the title or description, the framework will use the template "{{ obj }}" by default that is, the normal string representation of the object.

如果你在创建模板的时候,没有指明标题或者描述信息,框架会默认使用 "{{ obj }}" ,对象的字符串表示。

You can also change the names of these two templates by specifying title_template and description_template as attributes of your Feed class.

你也可以通过修改 Feed 类中的两个属性 title_templatedescription_template 来改变这两个模板的名字。

To specify the contents of <link> , you have two options. For each item in items() , Django first tries executing a get_absolute_url() method on that object. If that method doesnt exist, it tries calling a method item_link() in the Feed class, passing it a single parameter, item , which is the object itself.

你有两种方法来指定 <link> 的内容。 Django 首先执行 items() 中每一项的 get_absolute_url() 方法。 如果该方法不存在,就会尝试执行 Feed 类中的 item_link() 方法,并将自身作为 item 参数传递进去。

Both get_absolute_url() and item_link() should return the items URL as a normal Python string.

get_absolute_url()item_link() 都应该以Python字符串形式返回URL。

For the previous LatestEntries example, we could have very simple feed templates. latest_title.html contains:

对于前面提到的 LatestEntries 例子,我们可以实现一个简单的feed模板。 latest_title.html 包括:

{{ obj.title }}

and latest_description.html contains:

并且 latest_description.html 包含:

{{ obj.description }}

Its almost too easy

这真是 简单了!

A More Complex Feed


The framework also supports more complex feeds, via parameters.


For example, offers an RSS feed of recent crimes for every police beat in Chicago. It would be silly to create a separate Feed class for each police beat; that would violate the Dont Repeat Yourself (DRY) principle and would couple data to programming logic.

举个例子,chicagocrime.org提供了一个RSS源以跟踪每一片区域的犯罪近况。如果为每一个单独的区域建立一个 Feed 类就显得很不明智。这样做就违反了DRY原则了,程序逻辑也会和数据耦合在一起。

Instead, the syndication framework lets you make generic feeds that return items based on information in the feeds URL.

取而代之的方法是,使用聚合框架来产生一个通用的源,使其可以根据feeds URL返回相应的信息。

On, the police-beat feeds are accessible via URLs like this:


  • : Returns recent crimes for beat 0613

  • :返回0613号地区的犯罪数据

  • : Returns recent crimes for beat 1424

  • :返回1424号地区的犯罪数据

The slug here is "beats" . The syndication framework sees the extra URL bits after the slug 0613 and 1424 and gives you a hook to tell it what those URL bits mean and how they should influence which items get published in the feed.

固定的那一部分是 "beats" (区域)。聚合框架看到了后面的不同之处 06131424 ,它会提供给你一个钩子函数来描述这些URL的意义,以及会对feed中的项产生的影响。

An example makes this clear. Heres the code for these beat-specific feeds:


from django.core.exceptions import ObjectDoesNotExist

class BeatFeed(Feed):
    def get_object(self, bits):
        # In case of "/rss/beats/0613/foo/bar/baz/", or other such
        # clutter, check that bits has only one member.
        if len(bits) != 1:
            raise ObjectDoesNotExist
        return Beat.objects.get(beat__exact=bits[0])

    def title(self, obj):
        return " Crimes for beat %s" % obj.beat

    def link(self, obj):
        return obj.get_absolute_url()

    def description(self, obj):
        return "Crimes recently reported in police beat %s" % obj.beat

    def items(self, obj):
        crimes =  Crime.objects.filter(
        return crimes.order_by('-crime_date')[:30]

Heres the basic algorithm the RSS framework, given this class and a request to the URL /rss/beats/0613/ :

以下是RSS框架的基本算法,我们假设通过URL /rss/beats/0613/ 来访问这个类:

The framework gets the URL /rss/beats/0613/ and notices theres an extra bit of URL after the slug. It splits that remaining string by the slash character ("/" ) and calls the Feed classs get_object() method, passing it the bits.

框架获得了URL /rss/beats/0613/ 并且注意到URL中的slug部分后面含有更多的信息。它将斜杠("/" )作为分隔符,把剩余的字符串分割开作为参数,调用 Feed 类的 get_object() 方法。

In this case, bits is ['0613'] . For a request to /rss/beats/0613/foo/bar/ , bits would be ['0613', 'foo', 'bar'] .

在这个例子中,添加的信息是 ['0613'] 。对于 /rss/beats/0613/foo/bar/ 的一个URL请求, 这些信息就是 ['0613', 'foo', 'bar']

get_object() is responsible for retrieving the given beat, from the given bits .

get_object() 就根据给定的 bits 值来返回区域信息。

In this case, it uses the Django database API to retrieve the beat. Note that get_object() should raise django.core.exceptions.ObjectDoesNotExist if given invalid parameters. Theres no try /except around the Beat.objects.get() call, because its not necessary. That function raises Beat.DoesNotExist on failure, and Beat.DoesNotExist is a subclass of ObjectDoesNotExist . Raising ObjectDoesNotExist in get_object() tells Django to produce a 404 error for that request.

在这个例子中,它使用了Django的数据库API来获取信息。注意到如果给定的参数不合法, get_object() 会抛出 django.core.exceptions.ObjectDoesNotExist 异常。在 Beat.objects.get() 调用中也没有出现 try /except 代码块。函数在出错时抛出 Beat.DoesNotExist 异常,而 Beat.DoesNotExistObjectDoesNotExist 异常的一个子类型。 而在 get_object()

System Message: WARNING/2 (<string>, line 1583)

Block quote ends without a blank line; unexpected unindent.

中抛出 ObjectDoesNotExist 异常又会使得Django引发404错误。

To generate the feeds <title> , <link> , and <description> , Django uses the title() , link() , and description() methods. In the previous example, they were simple string class attributes, but this example illustrates that they can be either strings or methods. For each of title , link , and description , Django follows this algorithm:

为产生 <title><link> , 和 <description> 的feeds, Django使用 title() , link() , 和 description() 方法。 在上面的例子中,它们都是简单的字符串类型的类属性,而这个例子表明,它们既可以是字符串, 也可以是 方法。对于每一个 titlelinkdescription 的组合,Django使用以下的算法:

  1. It tries to call a method, passing the obj argument, where obj is the object returned by get_object() .

  1. 试图调用一个函数,并且以 get_object() 返回的对象作为参数传递给 obj 参数。

  1. Failing that, it tries to call a method with no arguments.

  1. 如果没有成功,则不带参数调用一个方法。

  1. Failing that, it uses the class attribute.

  1. 还不成功,则使用类属性。

Finally, note that items() in this example also takes the obj argument. The algorithm for items is the same as described in the previous step first, it tries items(obj) , then items() , and then finally an items class attribute (which should be a list).

最后,值得注意的是,这个例子中的 items() 使用 obj 参数。对于 items 的算法就如同上面第一步所描述的那样,首先尝试 items(obj) , 然后是 items() ,最后是 items 类属性(必须是一个列表)。

Full documentation of all the methods and attributes of the Feed classes is always available from the official Django documentation (

Feed 类所有方法和属性的完整文档,请参考官方的Django文档 ( 。

Specifying the Type of Feed


By default, the syndication framework produces RSS 2.0. To change that, add a feed_type attribute to your Feed class:

默认情况下, 聚合框架生成RSS 2.0. 要改变这样的情况, 在 Feed 类中添加一个 feed_type 属性.

from django.utils.feedgenerator import Atom1Feed

class MyFeed(Feed):
    feed_type = Atom1Feed

Note that you set feed_type to a class object, not an instance. Currently available feed types are shown in Table 11-1.

注意你把 feed_type 赋值成一个类对象,而不是类实例。目前合法的Feed类型如表11-1所示。

Table 11-1. Feed Types
Feed Class Format
django.utils.feedgenerator.Rss201rev2Feed RSS 2.01 (default)
django.utils.feedgenerator.RssUserland091Feed RSS 0.91
django.utils.feedgenerator.Atom1Feed Atom 1.0
表 11-1. Feed 类型
Feed 类 类型
django.utils.feedgenerator.Rss201rev2Feed RSS 2.01 (default)
django.utils.feedgenerator.RssUserland091Feed RSS 0.91
django.utils.feedgenerator.Atom1Feed Atom 1.0



To specify enclosures (i.e., media resources associated with a feed item such as MP3 podcast feeds), use the item_enclosure_url , item_enclosure_length , and item_enclosure_mime_type hooks, for example:

为了指定闭包(例如,与feed项比方说MP3 feeds相关联的媒体资源信息),使用 item_enclosure_urlitem_enclosure_length , 以及 item_enclosure_mime_type ,比如

from myproject.models import Song

class MyFeedWithEnclosures(Feed):
    title = "Example feed with enclosures"
    link = "/feeds/example-with-enclosures/"

    def items(self):
        return Song.objects.all()[:30]

    def item_enclosure_url(self, item):
        return item.song_url

    def item_enclosure_length(self, item):
        return item.song_length

    item_enclosure_mime_type = "audio/mpeg"

This assumes, of course, that youve created a Song object with song_url and song_length (i.e., the size in bytes) fields.

当然,你首先要创建一个包含有 song_urlsong_length (比如按照字节计算的长度)域的 Song 对象。



Feeds created by the syndication framework automatically include the appropriate <language> tag (RSS 2.0) or xml:lang attribute (Atom). This comes directly from your LANGUAGE_CODE setting.

聚合框架自动创建的Feed包含适当的 <language> 标签(RSS 2.0) 或 xml:lang 属性(Atom). 他直接来自于您的 LANGUAGE_CODE 设置.



The link method/attribute can return either an absolute URL (e.g., "/blog/" ) or a URL with the fully qualified domain and protocol (e.g., "" ). If link doesnt return the domain, the syndication framework will insert the domain of the current site, according to your SITE_ID setting.

link 方法/属性可以以绝对URL的形式(例如, "/blog/" )或者指定协议和域名的URL的形式返回(例如 "" )。如果 link 没有返回域名,聚合框架会根据 SITE_ID 设置,自动的插入当前站点的域信息。

Atom feeds require a <link rel="self"> that defines the feeds current location. The syndication framework populates this automatically, using the domain of the current site according to the SITE_ID setting.

Atom feeds需要 <link rel="self"> 指明feeds现在的位置。聚合框架根据 SITE_ID 的设置,使用站点的域名自动完成这些功能。

Publishing Atom and RSS Feeds in Tandem

同时发布Atom and RSS

Some developers like to make available both Atom and RSS versions of their feeds. Thats easy to do with Django: just create a subclass of your feed class and set the feed_type to something different. Then update your URLconf to add the extra versions. Heres a full example:

一些开发人员想 同时 支持Atom和RSS。这在Django中很容易实现:只需创建一个你的 feed 类的子类,然后修改 feed_type ,并且更新URLconf内容。下面是一个完整的例子:

from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem
from django.utils.feedgenerator import Atom1Feed

class RssSiteNewsFeed(Feed):
    title = " site news"
    link = "/sitenews/"
    description = "Updates on changes and additions to"

    def items(self):
        return NewsItem.objects.order_by('-pub_date')[:5]

class AtomSiteNewsFeed(RssSiteNewsFeed):
    feed_type = Atom1Feed

And heres the accompanying URLconf:


from django.conf.urls.defaults import *
from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed

feeds = {
    'rss': RssSiteNewsFeed,
    'atom': AtomSiteNewsFeed,

urlpatterns = patterns('',
    # ...
    (r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
        {'feed_dict': feeds}),
    # ...

The Sitemap Framework

Sitemap 框架

A sitemap is an XML file on your Web site that tells search engine indexers how frequently your pages change and how important certain pages are in relation to other pages on your site. This information helps search engines index your site.

sitemap 是你服务器上的一个XML文件,它告诉搜索引擎你的页面的更新频率和某些页面相对于其它页面的重要性。这个信息会帮助搜索引擎索引你的网站。

For example, heres a piece of the sitemap for Djangos Web site (

例如,这是 Django 网站(的一部分:

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="">

For more on sitemaps, see

需要了解更多有关 sitemaps 的信息, 请参见

The Django sitemap framework automates the creation of this XML file by letting you express this information in Python code. To create a sitemap, you just need to write a Sitemap class and point to it in your URLconf.

Django sitemap 框架允许你用 Python 代码来表述这些信息,从而自动创建这个XML文件。要创建一个 sitemap,你只需要写一个 Sitemap 类然后配置你的URLconf指向它。



To install the sitemap application, follow these steps:

要安装 sitemap 应用程序, 按下面的步骤进行:

  1. Add 'django.contrib.sitemaps' to your INSTALLED_APPS setting.

  1. 'django.contrib.sitemaps' 添加到您的 INSTALLED_APPS 设置中.

  1. Make sure 'django.template.loaders.app_directories.load_template_source' is in your TEMPLATE_LOADERS setting. Its in there by default, so youll need to change this only if youve changed that setting.

  1. 确保 'django.template.loaders.app_directories.load_template_source' 在您的 TEMPLATE_LOADERS 设置中。默认情况下它在那里, 所以, 如果你已经改变了那个设置的话, 只需要改回来即可。

  1. Make sure youve installed the sites framework (see Chapter 14).

  1. 确定您已经安装了 sites 框架 (参见第14章).



The sitemap application doesnt install any database tables. The only reason it needs to go into INSTALLED_APPS is so the load_template_source template loader can find the default templates.

sitemap 应用程序没有安装任何数据库表. 它需要加入到 INSTALLED_APPS 中的唯一原因是: 这样 load_template_source 模板加载器可以找到默认的模板.



To activate sitemap generation on your Django site, add this line to your URLconf:

要在您的Django站点中激活sitemap生成, 请在您的 URLconf 中添加这一行:

(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})

This line tells Django to build a sitemap when a client accesses /sitemap.xml .

这一行告诉 Django, 当客户访问 /sitemap.xml 的时候, 构建一个 sitemap.

The name of the sitemap file is not important, but the location is. Search engines will only index links in your sitemap for the current URL level and below. For instance, if sitemap.xml lives in your root directory, it may reference any URL in your site. However, if your sitemap lives at /content/sitemap.xml , it may only reference URLs that begin with /content/ .

sitemap文件的名字无关紧要,但是它在服务器上的位置却很重要。搜索引擎只索引你的sitemap中当前URL级别及其以下级别的链接。用一个实例来说,如果 sitemap.xml 位于你的根目录,那么它将引用任何的URL。然而,如果你的sitemap位于 /content/sitemap.xml ,那么它只引用以 /content/ 打头的URL。

The sitemap view takes an extra, required argument: {'sitemaps': sitemaps} . sitemaps should be a dictionary that maps a short section label (e.g., blog or news ) to its Sitemap class (e.g., BlogSitemap or NewsSitemap ). It may also map to an instance of a Sitemap class (e.g., BlogSitemap(some_var) ).

sitemap视图需要一个额外的必须的参数: {'sitemaps': sitemaps}sitemaps 应该是一个字典,它把一个短的块标签(例如, blognews )映射到它的 Sitemap 类(例如, BlogSitemapNewsSitemap )。它也可以映射到一个 Sitemap 类的实例(例如, BlogSitemap(some_var) )。

Sitemap Classes

Sitemap 类

A Sitemap class is a simple Python class that represents a section of entries in your sitemap. For example, one Sitemap class could represent all the entries of your Weblog, while another could represent all of the events in your events calendar.

Sitemap 类展示了一个进入地图站点简单的Python类片断.例如,一个 Sitemap 类能展现所有日志入口,而另外一个能够调度所有的日历事件。

In the simplest case, all these sections get lumped together into one sitemap.xml , but its also possible to use the framework to generate a sitemap index that references individual sitemap files, one per section (as described shortly).

在最简单的例子中,所有部分可以全部包含在一个 sitemap.xml 中,也可以使用框架来产生一个站点地图,为每一个独立的部分产生一个单独的站点文件。

Sitemap classes must subclass django.contrib.sitemaps.Sitemap . They can live anywhere in your code tree.

Sitemap 类必须是 django.contrib.sitemaps.Sitemap 的子类. 他们可以存在于您的代码树的任何地方。

For example, lets assume you have a blog system, with an Entry model, and you want your sitemap to include all the links to your individual blog entries. Heres how your Sitemap class might look:

例如假设你有一个blog系统,有一个 Entry 的model,并且你希望你的站点地图包含所有连到你的blog入口的超链接。你的 Sitemap 类很可能是这样的:

from django.contrib.sitemaps import Sitemap
from import Entry

class BlogSitemap(Sitemap):
    changefreq = "never"
    priority = 0.5

    def items(self):
        return Entry.objects.filter(is_draft=False)

    def lastmod(self, obj):
        return obj.pub_date

Declaring a Sitemap should look very similar to declaring a Feed ; thats by design.

声明一个 Sitemap 和声明一个 Feed 看起来很类似;这都是预先设计好的。

Like Feed classes, Sitemap members can be either methods or attributes. See the steps in the earlier A Complex Example section for more about how this works.

如同 Feed 类一样, Sitemap 成员也既可以是方法,也可以是属性。想要知道更详细的内容,请参见上文 《一个复杂的例子》章节。

A Sitemap class can define the following methods/attributes:

一个 Sitemap 类可以定义如下 方法/属性:

items (required ): Provides list of objects. The framework doesnt care what type of objects they are; all that matters is that these objects get passed to the location() , lastmod() , changefreq() , and priority() methods.

items (必需 ):提供对象列表。框架并不关心对象的 类型 ;唯一关心的是这些对象会传递给 location()lastmod()changefreq() ,和 priority() 方法。

location (optional): Gives the absolute URL for a given object. Here, absolute URL means a URL that doesnt include the protocol or domain. Here are some examples:

location (可选):给定对象的绝对URL。绝对URL不包含协议名称和域名。下面是一些例子:

  • Good: '/foo/bar/'

  • 好的: '/foo/bar/'

  • Bad: ''

  • 差的: ''

  • Bad: ''

  • 差的: ''

If location isnt provided, the framework will call the get_absolute_url() method on each object as returned by items() .

如果没有提供 location , 框架将会在每个 items() 返回的对象上调用 get_absolute_url() 方法.

lastmod (optional): The objects last modification date, as a Python datetime object.

lastmod (可选): 对象的最后修改日期, 作为一个Python datetime 对象.

changefreq (optional): How often the object changes. Possible values (as given by the Sitemaps specification) are as follows:

changefreq (可选):对象变更的频率。可选的值如下(详见Sitemaps文档):

  • 'always'

  • 'always'

  • 'hourly'

  • 'hourly'

  • 'daily'

  • 'daily'

  • 'weekly'

  • 'weekly'

  • 'monthly'

  • 'monthly'

  • 'yearly'

  • 'yearly'

  • 'never'

  • 'never'

priority (optional): A suggested indexing priority between 0.0 and 1.0 . The default priority of a page is 0.5 ; see the documentation for more about how priority works.

priority (可选):取值范围在 0.0 and 1.0 之间,用来表明优先级。默认值为 0.5 ;请详见 文档。



The sitemap framework provides a couple convenience classes for common cases. These are described in the sections that follow.



The django.contrib.sitemaps.FlatPageSitemap class looks at all flat pages defined for the current site and creates an entry in the sitemap. These entries include only the location attribute not lastmod , changefreq , or priority .

django.contrib.sitemaps.FlatPageSitemap 类涉及到站点中所有的flat page,并在sitemap中建立一个入口。但仅仅只包含 location 属性,不支持 lastmodchangefreq ,或者 priority

See Chapter 14 for more about flat pages.

参见第16章获取有关flat page的更多的内容.


The GenericSitemap class works with any generic views (see Chapter 9) you already have.

GenericSitemap 与所有的通用视图一同工作(详见第9章)。

To use it, create an instance, passing in the same info_dict you pass to the generic views. The only requirement is that the dictionary have a queryset entry. It may also have a date_field entry that specifies a date field for objects retrieved from the queryset . This will be used for the lastmod attribute in the generated sitemap. You may also pass priority and changefreq keyword arguments to the GenericSitemap constructor to specify these attributes for all URLs.

你可以如下使用它,创建一个实例,并通过 info_dict 传递给通用视图。唯一的要求是字典包含 queryset 这一项。也可以用 date_field 来指明从 queryset 中取回的对象的日期域。这会被用作站点地图中的 lastmod 属性。你也可以向 GenericSitemap 的构造函数传递 prioritychangefreq 来指定所有URL的相应属性。

Heres an example of a URLconf using both FlatPageSitemap and GenericSiteMap (with the hypothetical Entry object from earlier):

下面是一个使用 FlatPageSitemap and GenericSiteMap (包括前面所假定的 Entry 对象)的URLconf:

from django.conf.urls.defaults import *
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from import Entry

info_dict = {
    'queryset': Entry.objects.all(),
    'date_field': 'pub_date',

sitemaps = {
    'flatpages': FlatPageSitemap,
    'blog': GenericSitemap(info_dict, priority=0.6),

urlpatterns = patterns('',
    # some generic view using info_dict
    # ...

    # the sitemap
     {'sitemaps': sitemaps})

Creating a Sitemap Index


The sitemap framework also has the ability to create a sitemap index that references individual sitemap files, one per each section defined in your sitemaps dictionary. The only differences in usage are as follows:

sitemap框架同样可以根据 sitemaps 字典中定义的单独的sitemap文件来建立索引。用法区别如下:

  • You use two views in your URLconf: django.contrib.sitemaps.views.index and django.contrib.sitemaps.views.sitemap .

  • 您在您的URLconf 中使用了两个视图: django.contrib.sitemaps.views.indexdjango.contrib.sitemaps.views.sitemap .

  • The django.contrib.sitemaps.views.sitemap view should take a section keyword argument.

  • django.contrib.sitemaps.views.sitemap 视图需要带一个 section 关键字参数.

Here is what the relevant URLconf lines would look like for the previous example:

这里是前面的例子的相关的 URLconf 行看起来的样子:

 {'sitemaps': sitemaps}),

 {'sitemaps': sitemaps})

This will automatically generate a sitemap.xml file that references both sitemap-flatpages.xml and sitemap-blog.xml . The Sitemap classes and the sitemaps dictionary dont change at all.

这将自动生成一个 sitemap.xml 文件, 它同时引用 sitemap-flatpages.xmlsitemap-blog.xml . Sitemap 类和 sitemaps 目录根本没有更改.

Pinging Google


You may want to ping Google when your sitemap changes, to let it know to reindex your site. The framework provides a function to do just that: django.contrib.sitemaps.ping_google() .

当你的sitemap变化的时候,你会想通知Google,以便让它知道对你的站点进行重新索引。框架就提供了这样的一个函数: django.contrib.sitemaps.ping_google()



At the time this book was written, only Google responded to sitemap pings. However, its quite likely that Yahoo and/or MSN will soon support these pings as well.

在本书写成的时候, 只有Google可以响应sitemap更新通知。然而,Yahoo和MSN可能很快也会支持这些通知。

At that time, well likely change the name of ping_google() to something like ping_search_engines() , so make sure to check the latest sitemap documentation at

到那个时候,把“ping_google()”这个名字改成“ping_search_engines()”会比较好。所以还是到 去检查一下最新的站点地图文档。

ping_google() takes an optional argument, sitemap_url , which should be the absolute URL of your sites sitemap (e.g., '/sitemap.xml' ). If this argument isnt provided, ping_google() will attempt to figure out your sitemap by performing a reverse lookup on your URLconf.

ping_google() 有一个可选的参数 sitemap_url ,它应该是你的站点地图的URL绝对地址(例如: /sitemap.xml )。如果不提供该参数, ping_google() 将尝试通过反查你的URLconf来找到你的站点地图。

ping_google() raises the exception django.contrib.sitemaps.SitemapNotFound if it cannot determine your sitemap URL.

如果不能够确定你的sitemap URL, ping_google() 会引发 django.contrib.sitemaps.SitemapNotFound 异常。

One useful way to call ping_google() is from a models save() method:

我们可以通过模型中的 save() 方法来调用 ping_google()

from django.contrib.sitemaps import ping_google

class Entry(models.Model):
    # ...
    def save(self):
        super(Entry, self).save()
        except Exception:
            # Bare 'except' because we could get a variety
            # of HTTP-related exceptions.

A more efficient solution, however, would be to call ping_google() from a cron script or some other scheduled task. The function makes an HTTP request to Googles servers, so you may not want to introduce that network overhead each time you call save() .

一个更有效的解决方案是用 cron 脚本或任务调度表来调用 ping_google() ,该方法使用Http直接请求Google服务器,从而减少每次调用 save() 时占用的网络带宽。

Whats Next?


Next, well continue to dig deeper into all the nifty built-in tools Django gives you. Chapter 12 looks at all the tools you need to provide user-customized sites: sessions, users, and authentication.

下面, 我们要继续深入挖掘所有的Django给你的很好的内置工具。 在第12章,您将看到提供用户自定义站点所需要的所有工具: sessions, users 和authentication.



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 粤ICP备16122281号-1