标签:
通常当我们谈到开发网站时,主要谈论的是HTML。当然,Web远不只有HTML,我们在Web上用多种格式来发布数据:RSS、PDF、图片等。
到目前为止,我们的注意力都是放在常见 HTML代码生成上,但是在这一章中,我们将会对使用 Django生成其它格式的内容进行简要介绍。
Django拥有一些便利的内建工具帮助你生成常见的非HTML内容:
§ RSS/Atom聚合文件
§ 站点地图(一个XML格式文件,最初由Google开发,用于给搜索引擎提示线索)
我们稍后会逐一研究这些工具,不过首先让我们来了解些基础原理。
基础:视图和MIME类型
还记得第三章的内容吗?
一个视图函数(view function),或者简称view ,只不过是一个可以处理一个Web请求并且返回一个Web响应的Python函数。这个响应可以是一个Web页面的HTML内容,或者一个跳转,或者一个404错误,或者一个XML文档,或者一幅图片,或者映射到任何东西上。
更正式的说,一个Django视图函数必须
§ 接受一个HttpRequest实例作为它的第一个参数
§ 返回一个HttpResponse实例
从一个视图返回一个非 HTML内容的关键是在构造一个HttpResponse类时,需要指定mimetype参数。通过改变 MIME类型,我们可以告知浏览器将要返回的数据是另一种不同的类型。
下面我们以返回一张PNG图片的视图为例。为了使事情能尽可能的简单,我们只是读入一张存储在磁盘上的图片:
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")
就是这么简单。如果改变open()中的图片路径为一张真实图片的路径,那么就可以使用这个十分简单的视图来提供一张图片,并且浏览器可以正确的显示它。
另外我们必须了解的是”HttpResponse”对象应用了Python标准的文件应用程序界面(API)。这就是说你可以在Python(或第三方库)任何用到文件的地方使用”HttpResponse”实例。
下面将用 Django生成 CSV 文件为例,说明它的工作原理。
生成 CSV文件
CSV 是一种简单的数据格式,通常为电子表格软件所使用。它主要是由一系列的表格行组成,每行中单元格之间使用逗号(CSV是逗号分隔数值(comma-separated values)的缩写)隔开。例如,下面是以 CSV 格式记录的一些违规航班乘客的数据。
Year,Unruly Airline Passengers
1995,146
1996,184
1997,235
1998,200
1999,226
2000,251
2001,299
2002,273
2003,281
2004,304
2005,203
备注
前面的列表是真实的数据,数据由美国联邦航空管理处提供。具体内容请参见http://www.faa.gov/data_statistics/passengers_cargo/unruly_passengers/.
虽然 CSV看上去简单,以至于简单到这个格式甚至都没有正式的定义。但是不同的软件会生成和使用不同的 CSV的变种,在使用上会有一些不便。幸运的是, Python使用的是标准 CSV 库,csv,所以它更通用。
因为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
代码和注释可以说是很清楚,但还有一些事情需要特别注意:
§ 响应返回的是text/csvMIME类型(而非默认的text/html)。这会告诉浏览器,返回的文档是CSV文件。
§ 响应会有一个附加的Content-Disposition头部,它包含有CSV文件的文件名。这个头部(或者说,附加部分)会指示浏览器弹出对话框询问文件存放的位置(而不仅仅是显示)。这个文件名是任意的,它会用在浏览器的另存为对话框中。
§ 与创建CSV的应用程序界面(API)挂接是很容易的:只需将”response”作为第一个变量传递给”csv.writer”。csv.writer函数希望获得一个文件类的对象,”HttpResponse”正好能达成这个目的。
§ 调用writer.writerow,并且传递给它一个类似 list或者 tuple 的可迭代对象,就可以在 CSV 文件中写入一行。
§ CSV 模块考虑到了引用的问题,所以您不用担心逸出字符串中引号和逗号。只要把信息传递给writerow(),它会处理好所有的事情。
在任何需要返回非 HTML内容的时候,都需要经过以下几步:创建一个HttpResponse响应对象(需要指定特殊的 MIME类型)。将它作为参数传给一个需要文件的方法,然后返回这个响应。
下面是一些其它的例子
生成 PDF文件
便携文件格式 (PDF)是由 Adobe 开发的格式,主要用于呈现可打印的文档,包含有 pixel-perfect格式,嵌入字体以及2D矢量图像。PDF文件可以被认为是一份打印文档的数字等价物;实际上,PDF文件通常用于需要将文档交付给其他人去打印的场合。
可以方便的使用 Python和 Django 生成 PDF 文档需要归功于一个出色的开源库, ReportLab (http://www.reportlab.org/rl_toolkit.html)。动态生成 PDF 文件的好处是在不同的情况下,如不同的用户或者不同的内容,可以按需生成不同的 PDF文件。
下面的例子是使用 Django和 ReportLab 在 KUSports.com 上生成个性化的可打印的 NCAA 赛程表 (tournament brackets)。
安装 ReportLab
在生成 PDF文件之前,需要安装 ReportLab库。这通常是个很简单的过程:从http://www.reportlab.org/downloads.html下载并且安装这个库即可。
使用手册(原始的只有 PDF格式)可以从http://www.reportlab.org/rsrc/userguide.pdf下载,其中包含有一些其它的安装指南。
注意
如果使用的是一些新的Linux 发行版,则在安装前可以先检查包管理软件。多数软件包仓库中都加入了 ReportLab。
比如,如果使用(杰出的) Ubuntu发行版,只需要简单的apt-get install python-reportlab一行命令即可完成安装。
在 Python交互环境中导入这个软件包以检查安装是否成功。
>>> import reportlab
如果刚才那条命令没有出现任何错误,则表明安装成功。
编写视图
和 CSV 类似,由 Django 动态生成 PDF文件很简单,因为 ReportLab API同样可以使用类似文件对象。
下面是一个 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.
p.showPage()
p.save()
return response
需要注意以下几点:
§ 这里我们使用的 MIME类型是application/pdf。这会告诉浏览器这个文档是一个 PDF文档,而不是 HTML 文档。如果忽略了这个参数,浏览器可能会把这个文件看成 HTML文档,这会使浏览器的窗口中出现很奇怪的文字。
§ 使用 ReportLab的 API 很简单:只需要将response对象作为canvas.Canvas的第一个参数传入。Canvas类需要一个类似文件的对象,HttpResponse对象可以满足这个要求。
§ 所有后续的 PDF生成方法需要由 PDF 对象调用(在本例中是p),而不是response对象。
§ 最后需要对 PDF文件调用showPage()和save()方法(否则你会得到一个损坏的 PDF文件)。
复杂的 PDF文件
如果您在创建一个复杂的 PDF文档(或者任何较大的数据块),请使用cStringIO库存放临时生成的 PDF文件。cStringIO提供了一个用 C编写的类似文件对象的接口,从而可以使系统的效率最高。
下面是使用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.
p.showPage()
p.save()
# Get the value of the StringIO buffer and write it to the response.
response.write(temp.getvalue())
return response
其它的可能性
使用 Python可以生成许多其它类型的内容,下面介绍的是一些其它的想法和一些可以用以实现它们的库。
ZIP 文件:Python标准库中包含有zipfile模块,它可以读和写压缩的 ZIP文件。它可以用于按需生成一些文件的压缩包,或者在需要时压缩大的文档。如果是 TAR文件则可以使用标准库tarfile模块。
动态图片: Python图片处理库 (PIL;http://www.pythonware.com/products/pil/)是极好的生成图片(PNG, JPEG, GIF以及其它许多格式)的工具。它可以用于自动为图片生成缩略图,将多张图片压缩到单独的框架中,或者是做基于 Web的图片处理。
图表: Python有许多出色并且强大的图表库用以绘制图表,按需地图,表格等。我们不可能将它们全部列出,所以下面列出的是个中的翘楚。
§ matplotlib(http://matplotlib.sourceforge.net/)可以用于生成通常是由 matlab或者 Mathematica生成的高质量图表。
§ pygraphviz(https://networkx.lanl.gov/wiki/pygraphviz)是一个 Graphviz图形布局的工具 (http://graphviz.org/)的 Python 接口,可以用于生成结构化的图表和网络。
总之,所有可以写文件的库都可以与 Django同时使用。请相信一切皆有可能。
我们已经了解了生成“非HTML”内容的基本知识,让我们进一步总结一下。Django拥有很多用以生成各类“非HTML”内容的内置工具。
内容聚合器应用框架
Django带来了一个高级的聚合生成框架,它使得创建RSS和Atom feeds变得非常容易。
什么是RSS?什么是Atom?
RSS和Atom都是基于XML的格式,你可以用它来提供有关你站点内容的自动更新的feed。了解更多关于RSS的可以访问http://www.whatisrss.com/,更多Atom的信息可以访问http://www.atomenabled.org/.
想创建一个联合供稿的源(syndication feed),所需要做的只是写一个简短的python类。你可以创建任意多的源(feed)。
高级feed生成框架是一个默认绑定到/feeds/的视图,Django使用URL的其它部分(在/feeds/之后的任何东西)来决定输出哪个feed
要创建一个feed,您将创建一个Feed类,并在您的 URLconf 中指向它. ( 查看第3章和第8章,可以获取更多有关URLconfs的更多信息 )
初始化
为了在您的Django站点中激活syndication feeds,添加如下的 URLconf:
(r‘^feeds/(?P<url>.*)/$‘,
‘django.contrib.syndication.views.feed‘,
{‘feed_dict‘: feeds}
),
这一行告诉Django使用RSS框架处理所有的以"feeds/"开头的URL. ( 你可以修改"feeds/"前缀以满足您自己的要求. )
URLConf里有一行参数:``{‘feed_dict’: feeds}``,这个参数可以把对应URL需要发布的feed内容传递给 syndication framework
特别的,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}),
# ...
)
前面的例子注册了两个feed:
§ LatestEntries``表示的内容将对应到``feeds/latest/.
§ LatestEntriesByCategory``的内容将对应到 ``feeds/categories/.
以上的设定完成之后,接下来需要自己定义Feed类
一个Feed类是一个简单的python类,用来表示一个syndication feed.一个feed可能是简单的 (例如一个站点新闻feed,或者最基本的,显示一个blog的最新条目),也可能更加复杂(例如一个显示blog某一类别下所有条目的feed。这里类别 category 是个变量).
Feed类必须继承django.contrib.syndication.feeds.Feed,它们可以在你的代码树的任何位置
一个简单的Feed
例子来自于chicagocrime.org,描述最近5项新闻条目的feed:
from django.contrib.syndication.feeds import Feed
from chicagocrime.models import NewsItem
class LatestEntries(Feed):
title = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."
def items(self):
return NewsItem.objects.order_by(‘-pub_date‘)[:5]
要注意的重要的事情如下所示:
子类django.contrib.syndication.feeds.Feed.
title,link,和description对应一个标准 RSS里的<title>,<link>,和<description>标签.
items()是一个方法,返回一个用以包含在包含在feed的<item>元素里的 list 虽然例子里用Djangos database API返回的NewsItem对象,items()不一定必须返回 model的实例
你可以利用 Django models免费实现一定功能,但是items()可以返回你想要的任意类型的对象.
还有一个步骤,在一个RSS feed里,每个(item)有一个(title),(link)和(description),我们需要告诉框架把数据放到这些元素中
如果要指定<title>和<description>,可以建立一个Django模板(见Chapter 4)名字叫feeds/latest_title.html和feeds/latest_description.html,后者是URLConf里为对应feed指定的slug。注意.html后缀是必须的。
RSS系统模板渲染每一个条目,需要给传递2个参数给模板上下文变量:
§ obj:当前对象 ( 返回到items()任意对象之一 )。
§ site:一个表示当前站点的django.models.core.sites.Site对象。这对于{{ site.domain }}或者{{ site.name }}很有用。
如果你在创建模板的时候,没有指明标题或者描述信息,框架会默认使用"{{ obj }}",对象的字符串表示。
你也可以通过修改Feed类中的两个属性title_template和description_template来改变这两个模板的名字。
你有两种方法来指定<link>的内容。 Django首先执行items()中每一项的get_absolute_url()方法。如果该方法不存在,就会尝试执行Feed类中的item_link()方法,并将自身作为item参数传递进去。
get_absolute_url()和item_link()都应该以Python字符串形式返回URL。
对于前面提到的LatestEntries例子,我们可以实现一个简单的feed模板。latest_title.html包括:
{{ obj.title }}
并且latest_description.html包含:
{{ obj.description }}
这真是太简单了!
一个更复杂的Feed
框架通过参数支持更加复杂的feeds。
举个例子,chicagocrime.org提供了一个RSS源以跟踪每一片区域的犯罪近况。如果为每一个单独的区域建立一个Feed类就显得很不明智。这样做就违反了DRY原则了,程序逻辑也会和数据耦合在一起。
取而代之的方法是,使用聚合框架来产生一个通用的源,使其可以根据feeds URL返回相应的信息。
在chicagocrime这个例子中,区域信息可以通过这样的URL方式来访问:
§ http://www.chicagocrime.org/rss/beats/0613/:返回0613号地区的犯罪数据
§ http://www.chicagocrime.org/rss/beats/1424/:返回1424号地区的犯罪数据
固定的那一部分是"beats"(区域)。聚合框架看到了后面的不同之处0613和1424,它会提供给你一个钩子函数来描述这些URL的意义,以及会对feed中的项产生的影响。
举个例子会澄清一切。下面是每个地区特定的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 "Chicagocrime.org: 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(beat__id__exact=obj.id)
return crimes.order_by(‘-crime_date‘)[:30]
以下是RSS框架的基本算法,我们假设通过URL/rss/beats/0613/来访问这个类:
框架获得了URL/rss/beats/0613/并且注意到URL中的slug部分后面含有更多的信息。它将斜杠("/")作为分隔符,把剩余的字符串分割开作为参数,调用Feed类的get_object()方法。
在这个例子中,添加的信息是[‘0613‘]。对于/rss/beats/0613/foo/bar/的一个URL请求,这些信息就是[‘0613‘, ‘foo‘, ‘bar‘]。
get_object()就根据给定的bits值来返回区域信息。
在这个例子中,它使用了Django的数据库API来获取信息。注意到如果给定的参数不合法,get_object()会抛出django.core.exceptions.ObjectDoesNotExist异常。在Beat.objects.get()调用中也没有出现try/except代码块。函数在出错时抛出Beat.DoesNotExist异常,而Beat.DoesNotExist是ObjectDoesNotExist异常的一个子类型。而在get_object()
System Message: WARNING/2 (<string>, line 798)
Block quote ends without a blank line; unexpected unindent.
中抛出ObjectDoesNotExist异常又会使得Django引发404错误。
为产生<title>,<link>,和<description>的feeds, Django使用title(),link(),和description()方法。在上面的例子中,它们都是简单的字符串类型的类属性,而这个例子表明,它们既可以是字符串,也可以是方法。对于每一个title,link和description的组合,Django使用以下的算法:
1. 试图调用一个函数,并且以get_object()返回的对象作为参数传递给obj参数。
2. 如果没有成功,则不带参数调用一个方法。
3. 还不成功,则使用类属性。
最后,值得注意的是,这个例子中的items()使用obj参数。对于items的算法就如同上面第一步所描述的那样,首先尝试items(obj),然后是items(),最后是items类属性(必须是一个列表)。
Feed类所有方法和属性的完整文档,请参考官方的Django文档 (http://www.djangoproject.com/documentation/0.96/syndication_feeds/)。
指定Feed的类型
默认情况下,聚合框架生成RSS 2.0. 要改变这样的情况, 在Feed类中添加一个feed_type属性.
from django.utils.feedgenerator import Atom1Feed
class MyFeed(Feed):
feed_type = Atom1Feed
注意你把feed_type赋值成一个类对象,而不是类实例。目前合法的Feed类型如表11-1所示。
表 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 |
闭包
为了指定闭包(例如,与feed项比方说MP3 feeds相关联的媒体资源信息),使用item_enclosure_url,item_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"
当然,你首先要创建一个包含有song_url和song_length(比如按照字节计算的长度)域的Song对象。
语言
聚合框架自动创建的Feed包含适当的<language>标签(RSS 2.0)或xml:lang属性(Atom).他直接来自于您的LANGUAGE_CODE设置.
URLs
link方法/属性可以以绝对URL的形式(例如,"/blog/")或者指定协议和域名的URL的形式返回(例如"http://www.example.com/blog/")。如果link没有返回域名,聚合框架会根据SITE_ID设置,自动的插入当前站点的域信息。
Atom feeds需要<link rel="self">指明feeds现在的位置。聚合框架根据SITE_ID的设置,使用站点的域名自动完成这些功能。
同时发布Atom and RSS
一些开发人员想同时支持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 = "Chicagocrime.org site news"
link = "/sitenews/"
description = "Updates on changes and additions to chicagocrime.org."
def items(self):
return NewsItem.objects.order_by(‘-pub_date‘)[:5]
class AtomSiteNewsFeed(RssSiteNewsFeed):
feed_type = Atom1Feed
这是与之相对应那个的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}),
# ...
)
Sitemap 框架
sitemap是你服务器上的一个XML文件,它告诉搜索引擎你的页面的更新频率和某些页面相对于其它页面的重要性。这个信息会帮助搜索引擎索引你的网站。
例如,这是 Django网站(http://www.djangoproject.com/sitemap.xml)sitemap的一部分:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://www.djangoproject.com/documentation/</loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>http://www.djangoproject.com/documentation/0_90/</loc>
<changefreq>never</changefreq>
<priority>0.1</priority>
</url>
...
</urlset>
需要了解更多有关 sitemaps的信息, 请参见http://www.sitemaps.org/.
Django sitemap 框架允许你用 Python代码来表述这些信息,从而自动创建这个XML文件。要创建一个 sitemap,你只需要写一个Sitemap类然后配置你的URLconf指向它。
安装
要安装 sitemap应用程序, 按下面的步骤进行:
1. 将‘django.contrib.sitemaps‘添加到您的INSTALLED_APPS设置中.
2. 确保‘django.template.loaders.app_directories.load_template_source‘在您的TEMPLATE_LOADERS设置中。默认情况下它在那里,所以, 如果你已经改变了那个设置的话,只需要改回来即可。
3. 确定您已经安装了 sites框架 .
备注
sitemap 应用程序没有安装任何数据库表.它需要加入到INSTALLED_APPS中的唯一原因是:这样load_template_source模板加载器可以找到默认的模板.
初始化
要在您的Django站点中激活sitemap生成,请在您的 URLconf 中添加这一行:
(r‘^sitemap.xml$‘, ‘django.contrib.sitemaps.views.sitemap‘, {‘sitemaps‘: sitemaps})
这一行告诉 Django,当客户访问/sitemap.xml的时候,构建一个 sitemap.
sitemap文件的名字无关紧要,但是它在服务器上的位置却很重要。搜索引擎只索引你的sitemap中当前URL级别及其以下级别的链接。用一个实例来说,如果sitemap.xml位于你的根目录,那么它将引用任何的URL。然而,如果你的sitemap位于/content/sitemap.xml,那么它只引用以/content/打头的URL。
sitemap视图需要一个额外的必须的参数:{‘sitemaps‘: sitemaps}。sitemaps应该是一个字典,它把一个短的块标签(例如,blog或news)映射到它的Sitemap类(例如,BlogSitemap或NewsSitemap)。它也可以映射到一个Sitemap类的实例(例如,BlogSitemap(some_var))。
Sitemap 类
Sitemap类展示了一个进入地图站点简单的Python类片断.例如,一个Sitemap类能展现所有日志入口,而另外一个能够调度所有的日历事件。
在最简单的例子中,所有部分可以全部包含在一个sitemap.xml中,也可以使用框架来产生一个站点地图,为每一个独立的部分产生一个单独的站点文件。
Sitemap类必须是django.contrib.sitemaps.Sitemap的子类.他们可以存在于您的代码树的任何地方。
例如假设你有一个blog系统,有一个Entry的model,并且你希望你的站点地图包含所有连到你的blog入口的超链接。你的Sitemap类很可能是这样的:
from django.contrib.sitemaps import Sitemap
from mysite.blog.models 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
声明一个Sitemap和声明一个Feed看起来很类似;这都是预先设计好的。
如同Feed类一样,Sitemap成员也既可以是方法,也可以是属性。想要知道更详细的内容,请参见上文《一个复杂的例子》章节。
一个Sitemap类可以定义如下方法/属性:
items(必需):提供对象列表。框架并不关心对象的类型;唯一关心的是这些对象会传递给location(),lastmod(),changefreq(),和priority()方法。
location(可选):给定对象的绝对URL。绝对URL不包含协议名称和域名。下面是一些例子:
§ 好的:‘/foo/bar/‘
§ 差的:‘example.com/foo/bar/‘
§ 差的:‘http://example.com/foo/bar/‘
如果没有提供location,框架将会在每个items()返回的对象上调用get_absolute_url()方法.
lastmod(可选):对象的最后修改日期, 作为一个Pythondatetime对象.
changefreq(可选):对象变更的频率。可选的值如下(详见Sitemaps文档):
§ ‘always‘
§ ‘hourly‘
§ ‘daily‘
§ ‘weekly‘
§ ‘monthly‘
§ ‘yearly‘
§ ‘never‘
priority(可选):取值范围在0.0and1.0之间,用来表明优先级。默认值为0.5;请详见http://sitemaps.org文档。
快捷方式
sitemap框架提供了一些常用的类。在下一部分中会看到。
FlatPageSitemap
django.contrib.sitemaps.FlatPageSitemap类涉及到站点中所有的flat page,并在sitemap中建立一个入口。但仅仅只包含location属性,不支持lastmod,changefreq,或者priority。
参见第16章获取有关flat page的更多的内容.
GenericSitemap
GenericSitemap与所有的通用视图一同工作。
你可以如下使用它,创建一个实例,并通过info_dict传递给通用视图。唯一的要求是字典包含queryset这一项。也可以用date_field来指明从queryset中取回的对象的日期域。这会被用作站点地图中的lastmod属性。你也可以向GenericSitemap的构造函数传递priority和changefreq来指定所有URL的相应属性。
下面是一个使用FlatPageSitemapandGenericSiteMap(包括前面所假定的Entry对象)的URLconf:
from django.conf.urls.defaults import *
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
from mysite.blog.models 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
(r‘^sitemap.xml$‘,
‘django.contrib.sitemaps.views.sitemap‘,
{‘sitemaps‘: sitemaps})
)
创建一个Sitemap索引
sitemap框架同样可以根据sitemaps字典中定义的单独的sitemap文件来建立索引。用法区别如下:
§ 您在您的URLconf中使用了两个视图:django.contrib.sitemaps.views.index和django.contrib.sitemaps.views.sitemap.
§ django.contrib.sitemaps.views.sitemap视图需要带一个section关键字参数.
这里是前面的例子的相关的 URLconf行看起来的样子:
(r‘^sitemap.xml$‘,
‘django.contrib.sitemaps.views.index‘,
{‘sitemaps‘: sitemaps}),
(r‘^sitemap-(?P<section>.+).xml$‘,
‘django.contrib.sitemaps.views.sitemap‘,
{‘sitemaps‘: sitemaps})
这将自动生成一个sitemap.xml文件,它同时引用sitemap-flatpages.xml和sitemap-blog.xml.Sitemap类和sitemaps目录根本没有更改.
通知Google
当你的sitemap变化的时候,你会想通知Google,以便让它知道对你的站点进行重新索引。框架就提供了这样的一个函数:django.contrib.sitemaps.ping_google()。
备注
在本书写成的时候,只有Google可以响应sitemap更新通知。然而,Yahoo和MSN可能很快也会支持这些通知。
到那个时候,把“ping_google()”这个名字改成“ping_search_engines()”会比较好。所以还是到http://www.djangoproject.com/documentation/0.96/sitemaps/去检查一下最新的站点地图文档。
ping_google()有一个可选的参数sitemap_url,它应该是你的站点地图的URL绝对地址(例如:/sitemap.xml)。如果不提供该参数,ping_google()将尝试通过反查你的URLconf来找到你的站点地图。
如果不能够确定你的sitemap URL,ping_google()会引发django.contrib.sitemaps.SitemapNotFound异常。
我们可以通过模型中的save()方法来调用ping_google():
from django.contrib.sitemaps import ping_google
class Entry(models.Model):
# ...
def save(self):
super(Entry, self).save()
try:
ping_google()
except Exception:
# Bare ‘except‘ because we could get a variety
# of HTTP-related exceptions.
pass
一个更有效的解决方案是用cron脚本或任务调度表来调用ping_google(),该方法使用Http直接请求Google服务器,从而减少每次调用save()时占用的网络带宽。
转载请注明文章出处:http://blog.csdn.net/wolaiye320/article/details/51917602
标签:
原文地址:http://blog.csdn.net/wolaiye320/article/details/51917602