Django带有一个高聚合的框架让创造RSS 和 Atom更容易。
创建聚合feed,你要做的仅仅是写一个简短的Python类。你想要创造多少,就能创造多少feeds。
Django 也带有一个低等级的生成feed的API。如果你想要生成一个外部的Web内容或者其他普通的方式,你可以使用它。
一个 Feed类就是一个提供聚合种子的Python类。一个简单的种子(例如新闻的信息种子,或者只展示博客最新消息)更多功能的种子(例如展示博客中允许展示的特定类别的条目)。
Feed类继承自django.contrib.syndication.views.Feed。它们可以在你代码中的任意一处。
这个简单的示例,演示了某站点的最近五条新闻的记录:
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
from policebeat.models import NewsItem
class LatestEntriesFeed(Feed):
title = "Police beat site news"
link = "/sitenews/"
description = "Updates on changes and additions to police beat central."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
def item_title(self, item):
return item.title
def item_description(self, item):
return item.description
# item_link is only needed if NewsItem has no get_absolute_url method.
def item_link(self, item):
return reverse('news-item', args=[item.pk])
配置一个URL到这个feed,在URLconf中配置一个入口。例如:
from django.conf.urls import url
from myproject.feeds import LatestEntriesFeed
urlpatterns = [
# ...
url(r'^latest/feed/$', LatestEntriesFeed()),
# ...
]
注意:
还有一件事没做。在一个 RSS feed中, 每一个 <item> 都有一个<title>, <link> 和<description>. 我们需要告诉框架那些数据放进这些对象中。
在 <title>和<description>的内容中,Django尝试着在Feed类中召集item_title()和item_description()方法。他们会传入一个自己内部的单独的参数 item。这些都是可选的; 默认的是,unicode表示的对象都被使用了
如果你想要做一些特殊的格式化title或者description,Django templates可以帮助你。他们的路径会被Feed类中的title_template和description_template 参数做特殊处理。会通过模板内容的两个变量来返回每一条记录到模板中:
从下方的 a complex example 使用一个描述的模板。
如果您需要提供超过前面提到的两个变量,还有一种方法可以将附加信息传递到标题和描述模板。您可以在Feed子类中提供get_context_data方法的实现。例如:
from mysite.models import Article
from django.contrib.syndication.views import Feed
class ArticlesFeed(Feed):
title = "My articles"
description_template = "feeds/articles.html"
def items(self):
return Article.objects.order_by('-pub_date')[:5]
def get_context_data(self, **kwargs):
context = super(ArticlesFeed, self).get_context_data(**kwargs)
context['foo'] = 'bar'
return context
和模板:
Something about {{ foo }}: {{ obj.description }}
此方法将由items()返回的列表中的每个项目调用一次,并使用以下关键字参数:
get_context_data()的行为模仿了generic views的行为 - 你应该调用super()从父类中检索上下文数据,添加您的数据并返回修改后的字典。
要指定<link>的内容,您有两个选项。对于items()中的每个项目,Django首先尝试调用Feed类上的item_link()方法。以类似于标题和描述的方式,它传递单个参数item。如果该方法不存在,Django尝试对该对象执行get_absolute_url()方法。get_absolute_url()和item_link()应将项目的网址作为普通的Python字符串返回。与get_absolute_url()一样,item_link()的结果将直接包含在URL中,因此您负责对所有必要的URL进行引用并将其转换为ASCII方法本身。
该框架还通过参数支持更复杂的feed。
例如,网站可以为城市中的每个警察节拍提供最近的犯罪的RSS源。为每个警察节拍创建一个单独的Feed课;这将违反DRY principle并且将数据耦合到编程逻辑。相反,联合框架可让您访问从URLconf传递的参数,以便Feed可以根据Feed的网址中的信息输出项目。
警察打击饲料可以通过这样的URL访问:
这些可以与URLconf行匹配,例如:
url(r'^beats/(?P<beat_id>[0-9]+)/rss/$', BeatFeed()),
与视图类似,URL中的参数与请求对象一起传递到get_object()方法。
以下是这些特定于节拍的Feed的代码:
from django.contrib.syndication.views import FeedDoesNotExist
from django.shortcuts import get_object_or_404
class BeatFeed(Feed):
description_template = 'feeds/beat_description.html'
def get_object(self, request, beat_id):
return get_object_or_404(Beat, pk=beat_id)
def title(self, obj):
return "Police beat central: 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):
return Crime.objects.filter(beat=obj).order_by('-crime_date')[:30]
To generate the feed’s <title>, <link> and <description>, Django uses the title(), link() and description() methods. 在前面的示例中,它们是简单的字符串类属性,但是本示例说明它们可以是字符串或方法。对于title,link和description中的每一个,Django都遵循此算法:
还要注意,items()也遵循相同的算法 - 首先,它尝试items(obj),然后items() items类属性(它应该是一个列表)。
我们正在使用模板作为项目描述。它可以很简单:
{{ obj.description }}
但是,您可以根据需要自由添加格式。
下面的ExampleFeed类提供了有关Feed类的方法和属性的完整文档。
默认情况下,此框架中生成的Feed使用RSS 2.0。
要更改此属性,请向您的Feed类添加feed_type属性,如下所示:
from django.utils.feedgenerator import Atom1Feed
class MyFeed(Feed):
feed_type = Atom1Feed
请注意,您将feed_type设置为类对象,而不是实例。
目前可用的Feed类型有:
要指定机柜(例如用于创建Podcast Feed的机柜),请使用item_enclosure_url,item_enclosure_length和item_enclosure_mime_type挂钩。有关用法示例,请参见下面的ExampleFeed类。
由联合框架创建的Feed会自动包含适当的<language>标记(RSS 2.0)或xml:lang属性(Atom)。这直接来自您的LANGUAGE_CODE设置。
link方法/属性可以返回绝对路径(例如"/blog/")或具有完全限定域和协议的URL(例如"http://www.example.com/blog/")。如果link未返回域,则整合框架将根据您的SITE_ID setting。
Atom Feed需要定义Feed的当前位置的&lt; link rel =“self”&gt;。
有些开发人员喜欢提供Atom 和 RSS版本的Feed。使用Django很容易:只需创建Feed类的子类,并将feed_type设置为不同的类型即可。然后更新您的URLconf以添加额外的版本。
这里有一个完整的例子:
from django.contrib.syndication.views import Feed
from policebeat.models import NewsItem
from django.utils.feedgenerator import Atom1Feed
class RssSiteNewsFeed(Feed):
title = "Police beat site news"
link = "/sitenews/"
description = "Updates on changes and additions to police beat central."
def items(self):
return NewsItem.objects.order_by('-pub_date')[:5]
class AtomSiteNewsFeed(RssSiteNewsFeed):
feed_type = Atom1Feed
subtitle = RssSiteNewsFeed.description
注意
在此示例中,RSS提要使用description,而Atom提要使用subtitle。这是因为Atom Feed不提供Feed级“说明”,但他们提供“字幕”。
如果您在Feed类中提供description,Django将不会自动将其放入subtitle字幕和描述不一定是同一件事。而应定义subtitle属性。
在上面的示例中,我们只需将Atom Feed的subtitle设置为RSS Feed的description,因为它已经很短。
和附带的URLconf:
from django.conf.urls import url
from myproject.feeds import RssSiteNewsFeed, AtomSiteNewsFeed
urlpatterns = [
# ...
url(r'^sitenews/rss/$', RssSiteNewsFeed()),
url(r'^sitenews/atom/$', AtomSiteNewsFeed()),
# ...
]
此示例说明Feed类的所有可能的属性和方法:
from django.contrib.syndication.views import Feed
from django.utils import feedgenerator
class ExampleFeed(Feed):
# FEED TYPE -- Optional. This should be a class that subclasses
# django.utils.feedgenerator.SyndicationFeed. This designates
# which type of feed this should be: RSS 2.0, Atom 1.0, etc. If
# you don't specify feed_type, your feed will be RSS 2.0. This
# should be a class, not an instance of the class.
feed_type = feedgenerator.Rss201rev2Feed
# TEMPLATE NAMES -- Optional. These should be strings
# representing names of Django templates that the system should
# use in rendering the title and description of your feed items.
# Both are optional. If a template is not specified, the
# item_title() or item_description() methods are used instead.
title_template = None
description_template = None
# TITLE -- One of the following three is required. The framework
# looks for them in this order.
def title(self, obj):
"""
Takes the object returned by get_object() and returns the
feed's title as a normal Python string.
"""
def title(self):
"""
Returns the feed's title as a normal Python string.
"""
title = 'foo' # Hard-coded title.
# LINK -- One of the following three is required. The framework
# looks for them in this order.
def link(self, obj):
"""
# Takes the object returned by get_object() and returns the URL
# of the HTML version of the feed as a normal Python string.
"""
def link(self):
"""
Returns the URL of the HTML version of the feed as a normal Python
string.
"""
link = '/blog/' # Hard-coded URL.
# FEED_URL -- One of the following three is optional. The framework
# looks for them in this order.
def feed_url(self, obj):
"""
# Takes the object returned by get_object() and returns the feed's
# own URL as a normal Python string.
"""
def feed_url(self):
"""
Returns the feed's own URL as a normal Python string.
"""
feed_url = '/blog/rss/' # Hard-coded URL.
# GUID -- One of the following three is optional. The framework looks
# for them in this order. This property is only used for Atom feeds
# (where it is the feed-level ID element). If not provided, the feed
# link is used as the ID.
def feed_guid(self, obj):
"""
Takes the object returned by get_object() and returns the globally
unique ID for the feed as a normal Python string.
"""
def feed_guid(self):
"""
Returns the feed's globally unique ID as a normal Python string.
"""
feed_guid = '/foo/bar/1234' # Hard-coded guid.
# DESCRIPTION -- One of the following three is required. The framework
# looks for them in this order.
def description(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
description as a normal Python string.
"""
def description(self):
"""
Returns the feed's description as a normal Python string.
"""
description = 'Foo bar baz.' # Hard-coded description.
# AUTHOR NAME --One of the following three is optional. The framework
# looks for them in this order.
def author_name(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's name as a normal Python string.
"""
def author_name(self):
"""
Returns the feed's author's name as a normal Python string.
"""
author_name = 'Sally Smith' # Hard-coded author name.
# AUTHOR EMAIL --One of the following three is optional. The framework
# looks for them in this order.
def author_email(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's email as a normal Python string.
"""
def author_email(self):
"""
Returns the feed's author's email as a normal Python string.
"""
author_email = '[email protected]' # Hard-coded author email.
# AUTHOR LINK --One of the following three is optional. The framework
# looks for them in this order. In each case, the URL should include
# the "http://" and domain name.
def author_link(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
author's URL as a normal Python string.
"""
def author_link(self):
"""
Returns the feed's author's URL as a normal Python string.
"""
author_link = 'http://www.example.com/' # Hard-coded author URL.
# CATEGORIES -- One of the following three is optional. The framework
# looks for them in this order. In each case, the method/attribute
# should return an iterable object that returns strings.
def categories(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
categories as iterable over strings.
"""
def categories(self):
"""
Returns the feed's categories as iterable over strings.
"""
categories = ("python", "django") # Hard-coded list of categories.
# COPYRIGHT NOTICE -- One of the following three is optional. The
# framework looks for them in this order.
def feed_copyright(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
copyright notice as a normal Python string.
"""
def feed_copyright(self):
"""
Returns the feed's copyright notice as a normal Python string.
"""
feed_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
# TTL -- One of the following three is optional. The framework looks
# for them in this order. Ignored for Atom feeds.
def ttl(self, obj):
"""
Takes the object returned by get_object() and returns the feed's
TTL (Time To Live) as a normal Python string.
"""
def ttl(self):
"""
Returns the feed's TTL as a normal Python string.
"""
ttl = 600 # Hard-coded Time To Live.
# ITEMS -- One of the following three is required. The framework looks
# for them in this order.
def items(self, obj):
"""
Takes the object returned by get_object() and returns a list of
items to publish in this feed.
"""
def items(self):
"""
Returns a list of items to publish in this feed.
"""
items = ('Item 1', 'Item 2') # Hard-coded items.
# GET_OBJECT -- This is required for feeds that publish different data
# for different URL parameters. (See "A complex example" above.)
def get_object(self, request, *args, **kwargs):
"""
Takes the current request and the arguments from the URL, and
returns an object represented by this feed. Raises
django.core.exceptions.ObjectDoesNotExist on error.
"""
# ITEM TITLE AND DESCRIPTION -- If title_template or
# description_template are not defined, these are used instead. Both are
# optional, by default they will use the unicode representation of the
# item.
def item_title(self, item):
"""
Takes an item, as returned by items(), and returns the item's
title as a normal Python string.
"""
def item_title(self):
"""
Returns the title for every item in the feed.
"""
item_title = 'Breaking News: Nothing Happening' # Hard-coded title.
def item_description(self, item):
"""
Takes an item, as returned by items(), and returns the item's
description as a normal Python string.
"""
def item_description(self):
"""
Returns the description for every item in the feed.
"""
item_description = 'A description of the item.' # Hard-coded description.
def get_context_data(self, **kwargs):
"""
Returns a dictionary to use as extra context if either
description_template or item_template are used.
Default implementation preserves the old behavior
of using {'obj': item, 'site': current_site} as the context.
"""
# ITEM LINK -- One of these three is required. The framework looks for
# them in this order.
# First, the framework tries the two methods below, in
# order. Failing that, it falls back to the get_absolute_url()
# method on each item returned by items().
def item_link(self, item):
"""
Takes an item, as returned by items(), and returns the item's URL.
"""
def item_link(self):
"""
Returns the URL for every item in the feed.
"""
# ITEM_GUID -- The following method is optional. If not provided, the
# item's link is used by default.
def item_guid(self, obj):
"""
Takes an item, as return by items(), and returns the item's ID.
"""
# ITEM_GUID_IS_PERMALINK -- The following method is optional. If
# provided, it sets the 'isPermaLink' attribute of an item's
# GUID element. This method is used only when 'item_guid' is
# specified.
def item_guid_is_permalink(self, obj):
"""
Takes an item, as returned by items(), and returns a boolean.
"""
item_guid_is_permalink = False # Hard coded value
# ITEM AUTHOR NAME -- One of the following three is optional. The
# framework looks for them in this order.
def item_author_name(self, item):
"""
Takes an item, as returned by items(), and returns the item's
author's name as a normal Python string.
"""
def item_author_name(self):
"""
Returns the author name for every item in the feed.
"""
item_author_name = 'Sally Smith' # Hard-coded author name.
# ITEM AUTHOR EMAIL --One of the following three is optional. The
# framework looks for them in this order.
#
# If you specify this, you must specify item_author_name.
def item_author_email(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
author's email as a normal Python string.
"""
def item_author_email(self):
"""
Returns the author email for every item in the feed.
"""
item_author_email = '[email protected]' # Hard-coded author email.
# ITEM AUTHOR LINK -- One of the following three is optional. The
# framework looks for them in this order. In each case, the URL should
# include the "http://" and domain name.
#
# If you specify this, you must specify item_author_name.
def item_author_link(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
author's URL as a normal Python string.
"""
def item_author_link(self):
"""
Returns the author URL for every item in the feed.
"""
item_author_link = 'http://www.example.com/' # Hard-coded author URL.
# ITEM ENCLOSURE URL -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
def item_enclosure_url(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure URL.
"""
def item_enclosure_url(self):
"""
Returns the enclosure URL for every item in the feed.
"""
item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
# ITEM ENCLOSURE LENGTH -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
# In each case, the returned value should be either an integer, or a
# string representation of the integer, in bytes.
def item_enclosure_length(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure length.
"""
def item_enclosure_length(self):
"""
Returns the enclosure length for every item in the feed.
"""
item_enclosure_length = 32000 # Hard-coded enclosure length.
# ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
# publishing enclosures. The framework looks for them in this order.
def item_enclosure_mime_type(self, item):
"""
Takes an item, as returned by items(), and returns the item's
enclosure MIME type.
"""
def item_enclosure_mime_type(self):
"""
Returns the enclosure MIME type for every item in the feed.
"""
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
# ITEM PUBDATE -- It's optional to use one of these three. This is a
# hook that specifies how to get the pubdate for a given item.
# In each case, the method/attribute should return a Python
# datetime.datetime object.
def item_pubdate(self, item):
"""
Takes an item, as returned by items(), and returns the item's
pubdate.
"""
def item_pubdate(self):
"""
Returns the pubdate for every item in the feed.
"""
item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
# ITEM UPDATED -- It's optional to use one of these three. This is a
# hook that specifies how to get the updateddate for a given item.
# In each case, the method/attribute should return a Python
# datetime.datetime object.
def item_updateddate(self, item):
"""
Takes an item, as returned by items(), and returns the item's
updateddate.
"""
def item_updateddate(self):
"""
Returns the updateddated for every item in the feed.
"""
item_updateddate = datetime.datetime(2005, 5, 3) # Hard-coded updateddate.
# ITEM CATEGORIES -- It's optional to use one of these three. This is
# a hook that specifies how to get the list of categories for a given
# item. In each case, the method/attribute should return an iterable
# object that returns strings.
def item_categories(self, item):
"""
Takes an item, as returned by items(), and returns the item's
categories.
"""
def item_categories(self):
"""
Returns the categories for every item in the feed.
"""
item_categories = ("python", "django") # Hard-coded categories.
# ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
# following three is optional. The framework looks for them in this
# order.
def item_copyright(self, obj):
"""
Takes an item, as returned by items(), and returns the item's
copyright notice as a normal Python string.
"""
def item_copyright(self):
"""
Returns the copyright notice for every item in the feed.
"""
item_copyright = 'Copyright (c) 2007, Sally Smith' # Hard-coded copyright notice.
在后台,高级RSS框架使用较低级别的框架来生成订阅源的XML。此框架存在于单个模块中:django / utils / feedgenerator.py。
您自己使用此框架,用于生成较低级别的Feed。您还可以创建自定义Feed生成器子类,以与feed_type Feed选项一起使用。
feedgenerator模块包含基类:
和几个子类:
这三个类中的每一个都知道如何将某种类型的feed呈现为XML。他们共享这个接口:
使用给定的元数据字典初始化Feed,该元数据字典应用于整个Feed。必需的关键字参数为:
还有一堆其他可选关键字:
您传递给__init__的任何其他关键字参数将存储在self.feed中以与自定义Feed生成器配合使用。
所有参数都应该是Unicode对象,除了categories,它应该是Unicode对象序列。
向具有给定参数的Feed中添加项目。
必需的关键字参数为:
可选的关键字参数为:
将为自定义Feed生成器存储额外的关键字参数。
所有参数,如果给定,应该是Unicode对象,除了:
已添加可选的updateddate参数。
例如,要创建Atom 1.0订阅源并将其打印到标准输出:
>>> from django.utils import feedgenerator
>>> from datetime import datetime
>>> f = feedgenerator.Atom1Feed(
... title="My Weblog",
... link="http://www.example.com/",
... description="In which I write about what I ate today.",
... language="en",
... author_name="Myself",
... feed_url="http://example.com/atom.xml")
>>> f.add_item(title="Hot dog today",
... link="http://www.example.com/entries/1/",
... pubdate=datetime.now(),
... description="<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>")
>>> print(f.writeString('UTF-8'))
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
...
</feed>
如果您需要生成自定义Feed格式,您有几个选项。
如果Feed格式是完全自定义的,您需要将SyndicationFeed作为子类,并完全替换write()和writeString()方法。
但是,如果Feed格式是RSS或Atom分拆(即GeoRSS,Apple的iTunes podcast格式等),则您有了更好的选择。这些类型的Feed通常会向底层格式添加额外的元素和/或属性,并且有一组方法可以SyndicationFeed调用以获取这些额外的属性。因此,您可以对相应的Feed生成器类(Atom1Feed或Rss201rev2Feed)进行子类化,并扩展这些回调。他们是:
警告
如果您覆盖任何这些方法,请务必调用超类方法,因为它们为每种Feed格式添加了必需的元素。
例如,您可能开始实现iTunes RSS Feed生成器,如:
class iTunesFeed(Rss201rev2Feed):
def root_attributes(self):
attrs = super(iTunesFeed, self).root_attributes()
attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd'
return attrs
def add_root_elements(self, handler):
super(iTunesFeed, self).add_root_elements(handler)
handler.addQuickElement('itunes:explicit', 'clean')
显然,对于一个完整的自定义feed类,还有很多工作要做,但上面的例子应该展示基本的想法。
2015年5月13日