通常當我們談到開發網站時,主要談論的是HTML。 當然,Web遠不只有HTML,我們在Web上用多種格式來發布數據: RSS、PDF、圖片等。
到目前為止,我們的注意力都是放在常見 HTML 代碼生成上,但是在這一章中,我們將會對使用 Django 生成其它格式的內容進行簡要介紹。
Django擁有一些便利的內建工具幫助你生成常見的非HTML內容:
我們稍后會逐一研究這些工具,不過首先讓我們來了解些基礎原理。
回顧一下第三章,視圖函數只是一個以Web請求為參數并返回Web響應的Python函數。 這個響應可以是一個Web頁面的HTML內容,或者一個跳轉,或者一個404 錯誤,或者一個XML文檔,或者一幅圖片,或者映射到任何東西上。
更正式的說,一個Django視圖函數 必須
從一個視圖返回一個非 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 是 逗號分隔數值(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
2006,134
2007,147
備注
前面的列表包含真實數據。 這些數據來自美國 聯邦航空管理局。
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/csv MIME類型(而非默認的 text/html )。這會告訴瀏覽器,返回的文檔是CSV文件。
響應會有一個附加的 Content-Disposition 頭部,它包含有CSV文件的文件名。 這個頭部(或者說,附加部分)會指示瀏覽器彈出對話框詢問文件存放的位置(而不僅僅是顯示)。 這個文件名是任意的。 它會顯示在瀏覽器的另存為對話框中。
要在HttpResponse指定頭部信息,只需把HttpResponse當做字典使用就可以了。
與創建CSV的應用程序界面(API)掛接是很容易的: 只需將 response 作為第一個變量傳遞給 csv.writer。 csv.writer 函數需要一個文件類的對象, HttpResponse 正好能達成這個目的。
調用 writer.writerow ,并且傳遞給它一個類似 list 或者 tuple 的可迭代對象,就可以在 CSV 文件中寫入一行。
CSV 模塊考慮到了引用的問題,所以您不用擔心逸出字符串中引號和逗號。 只要把信息傳遞給 writerow(),它會處理好所有的事情。
在任何需要返回非 HTML 內容的時候,都需要經過以下幾步: 創建一個 HttpResponse 響應對象(需要指定特殊的 MIME 類型),它它傳給需要處理文件的函數,然后返回這個響應對象。
下面是一些其它的例子。
便攜文檔格式 (PDF) 是由 Adobe 開發的格式,主要用于呈現可打印的文檔,其中包含有 pixel-perfect 格式,嵌入字體以及2D矢量圖像。 You can think of a PDF document as the digital equivalent of a printed document; indeed, PDFs are often used in distributing documents for the purpose of printing them.
可以方便的使用 Python 和 Django 生成 PDF 文檔需要歸功于一個出色的開源庫, ReportLab (http://www.reportlab.org/rl_toolkit.html) 。動態生成 PDF 文件的好處是在不同的情況下,如不同的用戶或者不同的內容,可以按需生成不同的 PDF 文件。 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.
下面的例子是使用 Django 和 ReportLab 在 KUSports.com 上生成個性化的可打印的 NCAA 賽程表 (tournament brackets) 。
在生成 PDF 文件之前,需要安裝 ReportLab 庫。這通常是個很簡單的過程: Its usually simple: just download and install the library from http://www.reportlab.org/downloads.html.
Note
如果使用的是一些新的 Linux 發行版,則在安裝前可以先檢查包管理軟件。 多數軟件包倉庫中都加入了 ReportLab 。
比如,如果使用(杰出的) Ubuntu 發行版,只需要簡單的 apt-get install python-reportlab 一行命令即可完成安裝。
使用手冊(原始的只有 PDF 格式)可以從 http://www.reportlab.org/rsrc/userguide.pdf 下載,其中包含有一些其它的安裝指南。
在 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 文檔,這會使瀏覽器的窗口中出現很奇怪的文字。 If you leave off this information, browsers will probably interpret the response as HTML, which will result in scary gobbledygook in the browser window.
使用 ReportLab 的 API 很簡單: 只需要將 response 對象作為 canvas.Canvas 的第一個參數傳入。
所有后續的 PDF 生成方法需要由 PDF 對象調用(在本例中是 p ),而不是 response 對象。
如果您在創建一個復雜的 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 可以生成許多其它類型的內容,下面介紹的是一些其它的想法和一些可以用以實現它們的庫。 Here are a few more ideas and some pointers to libraries you could use to implement them:
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 同時使用。 The possibilities are immense.
我們已經了解了生成“非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 Django uses the remainder of the URL (everything after /feeds/ ) to determine which feed to return.
要創建一個 sitemap,你只需要寫一個 Sitemap 類然后配置你的URLconf指向它。
為了在您的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,這里是一個完整的例子 You can define the feed_dict in the URLconf itself. Here’s a full example URLconf:
from django.conf.urls.defaults import *
from mysite.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/ .
的內容將對應到
feeds/categories/ .以上的設定完成之后,接下來需要自己定義 Feed 類
一個 Feed 類是一個簡單的python類,用來表示一個syndication feed. 一個feed可能是簡單的 (例如一個站點新聞feed,或者最基本的,顯示一個blog的最新條目),也可能更加復雜(例如一個顯示blog某一類別下所有條目的feed。 這里類別 category 是個變量).
Feed類必須繼承django.contrib.syndication.feeds.Feed,它們可以在你的代碼樹的任何位置
This simple example describes a feed of the latest five blog entries for a given blog:
from django.contrib.syndication.feeds import Feed
from mysite.blog.models import Entry
class LatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
要注意的重要的事情如下所示:
子類 django.contrib.syndication.feeds.Feed .
title , link , 和 description 對應一個標準 RSS 里的 , , 和 標簽.
還有一個步驟,在一個RSS feed里,每個(item)有一個(title),(link)和(description),我們需要告訴框架 把數據放到這些元素中 In an RSS feed, each has a , , and . We need to tell the framework what data to put into those elements.
如果要指定 和 ,可以建立一個Django模板(見Chapter 4)名字叫feeds/latest_title.html 和 feeds/latest_description.html ,后者是URLConf里為對應feed指定的slug 。注意 .html 后綴是必須的。 Note that the .html extension is required.
RSS系統模板渲染每一個條目,需要給傳遞2個參數給模板上下文變量:
obj : 當前對象 ( 返回到 items() 任意對象之一 )。
- site : 一個表示當前站點的 django.models.core.sites.Site 對象。 這對于 {{ site.domain }} 或者{{ site.name }} 很有用。
如果你在創建模板的時候,沒有指明標題或者描述信息,框架會默認使用 "{{ obj }}" ,對象的字符串表示。 (For model objects, this will be the unicode() method.
你也可以通過修改 Feed 類中的兩個屬性 title_template 和 description_template 來改變這兩個模板的名字。
你有兩種方法來指定 的內容。 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 }}
這真是 太 簡單了!
框架通過參數支持更加復雜的feeds。
For example, say your blog offers an RSS feed for every distinct tag you’ve used to categorize your entries. 如果為每一個單獨的區域建立一個 Feed 類就顯得很不明智。
取而代之的方法是,使用聚合框架來產生一個通用的源,使其可以根據feeds URL返回相應的信息。
Your tag-specific feeds could use URLs like this:
http://example.com/feeds/tags/python/ : Returns recent entries tagged with python
固定的那一部分是 "beats" (區域)。
舉個例子會澄清一切。 下面是每個地區特定的feeds:
from django.core.exceptions import ObjectDoesNotExist
from mysite.blog.models import Entry, Tag
class TagFeed(Feed):
def get_object(self, bits):
# In case of "/feeds/tags/cats/dogs/mice/", or other such
# clutter, check that bits has only one member.
if len(bits) != 1:
raise ObjectDoesNotExist
return Tag.objects.get(tag=bits[0])
def title(self, obj):
return "My Blog: Entries tagged with %s" % obj.tag
def link(self, obj):
return obj.get_absolute_url()
def description(self, obj):
return "Entries tagged with %s" % obj.tag
def items(self, obj):
entries = Entry.objects.filter(tags__id__exact=obj.id)
return entries.order_by('-pub_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 值來返回區域信息。
In this case, it uses the Django database API to retrieve the Tag . Note that get_object() should raisedjango.core.exceptions.ObjectDoesNotExist if given invalid parameters. 在 Beat.objects.get() 調用中也沒有出現 try /except 代碼塊。 函數在出錯時拋出 Beat.DoesNotExist 異常,而 Beat.DoesNotExist是 ObjectDoesNotExist 異常的一個子類型。
為產生 , , 和 的feeds, Django使用 title() , link() , 和description() 方法。 在上面的例子中,它們都是簡單的字符串類型的類屬性,而這個例子表明,它們既可以是字符串, 也可以是 方法。 對于每一個 title , link 和 description 的組合,Django使用以下的算法:
試圖調用一個函數,并且以 get_object() 返回的對象作為參數傳遞給 obj 參數。
如果沒有成功,則不帶參數調用一個方法。
- 還不成功,則使用類屬性。
最后,值得注意的是,這個例子中的 items() 使用 obj 參數。 對于 items 的算法就如同上面第一步所描述的那樣,首先嘗試 items(obj) , 然后是 items() ,最后是 items 類屬性(必須是一個列表)。
Feed 類所有方法和屬性的完整文檔,請參考官方的Django文檔 (http://www.djangoproject.com/documentation/0.96/syndication_feeds/) 。
默認情況下, 聚合框架生成RSS 2.0. 要改變這樣的情況, 在 Feed 類中添加一個 feed_type 屬性. To change that, add a feed_type attribute to your Feed class:
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包含適當的 標簽(RSS 2.0) 或 xml:lang 屬性(Atom). 他直接來自于您的LANGUAGE_CODE 設置. This comes directly from your LANGUAGE_CODE setting.
link 方法/屬性可以以絕對URL的形式(例如, "/blog/" )或者指定協議和域名的URL的形式返回(例如"http://www.example.com/blog/" )。如果 link 沒有返回域名,聚合框架會根據 SITE_ID 設置,自動的插入當前站點的域信息。 (See Chapter 16 for more on SITE_ID and the sites framework.)
Atom feeds需要 rel="self">
指明feeds現在的位置。 The syndication framework populates this automatically.
一些開發人員想 同時 支持Atom和RSS。 這在Django中很容易實現: 只需創建一個你的 feed 類的子類,然后修改 feed_type ,并且更新URLconf內容。 下面是一個完整的例子: Here’s a full example:
from django.contrib.syndication.feeds import Feed
from django.utils.feedgenerator import Atom1Feed
from mysite.blog.models import Entry
class RssLatestEntries(Feed):
title = "My Blog"
link = "/archive/"
description = "The latest news about stuff."
def items(self):
return Entry.objects.order_by('-pub_date')[:5]
class AtomLatestEntries(RssLatestEntries):
feed_type = Atom1Feed
這是與之相對應那個的URLconf:
from django.conf.urls.defaults import *
from myproject.feeds import RssLatestEntries, AtomLatestEntries
feeds = {
'rss': RssLatestEntries,
'atom': AtomLatestEntries,
}
urlpatterns = patterns('',
(r'^feeds/(?P.*)//pre>, 'django.contrib.syndication.views.feed',
{'feed_dict': feeds}),
# ...
)
sitemap 是你服務器上的一個XML文件,它告訴搜索引擎你的頁面的更新頻率和某些頁面相對于其它頁面的重要性。 這個信息會幫助搜索引擎索引你的網站。
例如,這是 Django 網站(http://www.djangoproject.com/sitemap.xml)sitemap的一部分:
http://www.djangoproject.com/documentation/
weekly
0.5
http://www.djangoproject.com/documentation/0_90/
never
0.1
...
需要了解更多有關 sitemaps 的信息, 請參見 http://www.sitemaps.org/.
Django sitemap 框架允許你用 Python 代碼來表述這些信息,從而自動創建這個XML文件。 要創建一個站點地圖,你只需要寫一個Sitemap
類,并且在URLconf中指向它。
要安裝 sitemap 應用程序, 按下面的步驟進行:
將 'django.contrib.sitemaps' 添加到您的 INSTALLED_APPS 設置中.
確保 'django.template.loaders.app_directories.load_template_source' 在您的 TEMPLATE_LOADERS 設置中。 默認情況下它在那里, 所以, 如果你已經改變了那個設置的話, 只需要改回來即可。
Note
sitemap 應用程序沒有安裝任何數據庫表. 它需要加入到 INSTALLED_APPS 中的唯一原因是: 這樣load_template_source 模板加載器可以找到默認的模板. The only reason it needs to go intoINSTALLED_APPS is so the load_template_source template loader can find the default templates.
要在您的Django站點中激活sitemap生成, 請在您的 URLconf 中添加這一行:
(r'^sitemap.xml/pre>, 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
This line tells Django to build a sitemap when a client accesses /sitemap.xml . Note that the dot character in sitemap.xml is escaped with a backslash, because dots have a special meaning in regular expressions.
sitemap文件的名字無關緊要,但是它在服務器上的位置卻很重要。 搜索引擎只索引你的sitemap中當前URL級別及其以下級別的鏈接。 用一個實例來說,如果 sitemap.xml 位于你的根目錄,那么它將引用任何的URL。 然而,如果你的sitemap位于 /content/sitemap.xml ,那么它只引用以 /content/ 打頭的URL。
sitemap視圖需要一個額外的必須的參數: {'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 類展示了一個進入地圖站點簡單的Python類片斷.例如,一個 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.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/' '/foo/bar/'
差的: 'example.com/foo/bar/' 'example.com/foo/bar/'
- Bad: 'http://example.com/foo/bar/'
如果沒有提供 location , 框架將會在每個 items() 返回的對象上調用 get_absolute_url() 方法.
lastmod (可選): 對象的最后修改日期, 作為一個Python datetime 對象. The object’s last modification date, as a Python datetime object.
changefreq (可選): 對象變更的頻率。 可選的值如下(詳見Sitemaps文檔):
'always'
'hourly'
'daily'
'weekly'
'monthly'
'yearly'
- 'never'
priority (可選): 取值范圍在 0.0 and 1.0 之間,用來表明優先級。
sitemap框架提供了一些常用的類。 在下一部分中會看到。
django.contrib.sitemaps.FlatPageSitemap 類涉及到站點中所有的flat page,并在sitemap中建立一個入口。 但僅僅只包含 location 屬性,不支持 lastmod , changefreq ,或者 priority 。
參見第16章獲取有關flat page的更多的內容.
GenericSitemap 與所有的通用視圖一同工作(詳見第9章)。
你可以如下使用它,創建一個實例,并通過 info_dict 傳遞給通用視圖。 唯一的要求是字典包含 queryset 這一項。 也可以用 date_field 來指明從 queryset 中取回的對象的日期域。 這會被用作站點地圖中的 lastmod屬性。
下面是一個使用 FlatPageSitemap and GenericSiteMap (包括前面所假定的 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('',
# ...
# the sitemap
(r'^sitemap\.xml/pre>,
'django.contrib.sitemaps.views.sitemap',
{'sitemaps': sitemaps})
)
sitemap框架同樣可以根據 sitemaps 字典中定義的單獨的sitemap文件來建立索引。 用法區別如下:
您在您的URLconf 中使用了兩個視圖: django.contrib.sitemaps.views.index 和django.contrib.sitemaps.views.sitemap . django.contrib.sitemaps.views.index
和django.contrib.sitemaps.views.sitemap
這里是前面的例子的相關的 URLconf 行看起來的樣子:
(r'^sitemap.xml/pre>,
'django.contrib.sitemaps.views.index',
{'sitemaps': sitemaps}),
(r'^sitemap-(?P.+).xml/pre>,
'django.contrib.sitemaps.views.sitemap',
{'sitemaps': sitemaps})
這將自動生成一個 sitemap.xml 文件, 它同時引用 sitemap-flatpages.xml 和 sitemap-blog.xml . Sitemap 類和sitemaps 目錄根本沒有更改.
當你的sitemap變化的時候,你會想通知Google,以便讓它知道對你的站點進行重新索引。 框架就提供了這樣的一個函數: django.contrib.sitemaps.ping_google() 。
ping_google() 有一個可選的參數 sitemap_url ,它應該是你的站點地圖的URL絕對地址(例如:
如果不能夠確定你的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, *args, **kwargs):
super(Entry, self).save(*args, **kwargs)
try:
ping_google()
except Exception:
# Bare 'except' because we could get a variety
# of HTTP-related exceptions.
pass
一個更有效的解決方案是用 cron 腳本或任務調度表來調用 ping_google() ,該方法使用Http直接請求Google服務器,從而減少每次調用 save() 時占用的網絡帶寬。 The function makes an HTTP request to Google’s servers, so you may not want to introduce that network overhead each time you call save().
Finally, if 'django.contrib.sitemaps' is in your INSTALLED_APPS , then your manage.py will include a new command, ping_google . This is useful for command-line access to pinging. For example:
python manage.py ping_google /sitemap.xml
下面, 我們要繼續深入挖掘所有的Django給你的很好的內置工具。 第十四章查看創建用戶自定義站點需要的工具 sessions, users 和authentication.