Feed 聚合框架

Django 提供了一个高级的 feed 聚合生成框架来创建 RSSAtom feed。

要创建任何聚合 feed,你只需要写一个简短的 Python 类。你可以创建任何你想要的 feed。

Django 还提供了一个低级别的 feed 生成 API。如果你想在网络内容之外或以其他较低级别的方式生成 feed,可以使用这个 API。

高级框架

概况

高级 feed 生成框架由 Feed 类提供。要创建一个 feed,需要写一个 Feed 类,并在你的 URLconf 中指向它的一个实例。

Feed

Feed 类是一个 Python 类,它表示一个聚合 feed。Feed 可以是简单的(例如,一个“站点新闻”feed,或者一个显示博客最新条目的基本 feed),也可以是更复杂的(例如,一个显示特定类别中所有博客条目的 feed,其中类别是可变的)。

Feed 类子类 django.contrib.syndication.views.Feed。它们可以存在于你代码库的任何地方。

Feed 类的实例是可以在你的 URLconf 中使用的视图。

一个简单的例子

这个简单的例子,取自于一个假设的警拍新闻网站,描述了一个最新的五个新闻项目的 feed:

  1. from django.contrib.syndication.views import Feed
  2. from django.urls import reverse
  3. from policebeat.models import NewsItem
  4. class LatestEntriesFeed(Feed):
  5. title = "Police beat site news"
  6. link = "/sitenews/"
  7. description = "Updates on changes and additions to police beat central."
  8. def items(self):
  9. return NewsItem.objects.order_by("-pub_date")[:5]
  10. def item_title(self, item):
  11. return item.title
  12. def item_description(self, item):
  13. return item.description
  14. # item_link is only needed if NewsItem has no get_absolute_url method.
  15. def item_link(self, item):
  16. return reverse("news-item", args=[item.pk])

要连接到这个 feed 的 URL,在你的 URLconf 中放入一个 Feed 对象的实例。例如:

  1. from django.urls import path
  2. from myproject.feeds import LatestEntriesFeed
  3. urlpatterns = [
  4. # ...
  5. path("latest/feed/", LatestEntriesFeed()),
  6. # ...
  7. ]

注意:

  • Feed 类的子类 django.contrib.syndication.views.Feed
  • titlelinkdescription 分别对应标准的 RSS <title><link><description> 元素。
  • items() 是一个返回一个应该作为 <item> 元素包含在 feed 中的对象列表的方法。虽然这个例子使用 Django 的 对象关系映射器 返回 NewsItem 对象,但是 items() 并不是一定要返回模型实例。虽然你通过使用Django模型得到了一些“免费”的功能,但 items() 可以返回任何类型的对象。
  • 如果你正在创建一个 Atom feed,而不是 RSS feed,请设置 subtitle 属性而不是 description 属性。参见后面的 同时发布 Atom 和 RSS feeds 的例子。

还剩下一件事要做。在一个 RSS Feed 中,每个 <item> 都有一个 <title><link><description>。我们需要告诉框架在这些元素中放入哪些数据。

  • 对于 <title><description> 的内容,Django 尝试在 Feed 类上调用方法 item_title()item_description()。它们会传递一个单一的参数 item,也就是对象本身。这些参数是可选的;默认情况下,两者都使用对象的字符串表示。

    如果你想对标题或描述进行特定格式化,可以使用 Django 模板 来代替。它们的路径可以通过 Feed 类中的 title_templatedescription_template 属性来指定。每个项目的模板都会被渲染,并传递两个模板上下文变量。

    请看下面使用描述模板的 一个复杂的例子

    Feed.get_context_data(**kwargs)

    如果你需要提供比前面提到的两个变量更多的信息,还有一种方法可以向标题和描述模板传递额外的信息。你可以在你的 Feed 子类中提供 get_context_data 方法的实现。例如:

    ``` from mysite.models import Article from django.contrib.syndication.views import Feed

  1. class ArticlesFeed(Feed):
  2. title = "My articles"
  3. description_template = "feeds/articles.html"
  4. def items(self):
  5. return Article.objects.order_by("-pub_date")[:5]
  6. def get_context_data(self, **kwargs):
  7. context = super().get_context_data(**kwargs)
  8. context["foo"] = "bar"
  9. return context
  10. ```
  11. 还有模板:
  12. ```
  13. Something about {{ foo }}: {{ obj.description }}
  14. ```
  15. 本方法将对 `items()` 返回的列表中的每个项目调用一次,其关键字参数如下:
  16. - `item`:当前项目。出于向后兼容的原因,这个上下文变量的名称是 `{{ obj }}`。
  17. - `obj`:由 `get_object()` 返回的对象。默认情况下,它不会暴露在模板中,以避免与 `{{ obj }}` 混淆(见上文),但你可以在实现 `get_context_data()` 时使用它。
  18. - `site`:如上所述的当前站点。
  19. - `request`:当前请求。
  20. `get_context_data()` 的行为模仿 [通用视图]($60b58a3323eb02d5.md#adding-extra-context)——你应该调用 `super()` 从父类中获取上下文数据,添加你的数据并返回修改后的字典。
  • 要指定 <link> 的内容,你有两个选择。对于 items() 中的每一个项目,Django 首先尝试调用 Feed 类上的 item_link() 方法。以类似于标题和描述的方式,给它传递一个单一的参数,item。如果该方法不存在,Django 会尝试在该对象上执行一个 get_absolute_url() 方法。get_absolute_url()item_link() 都应该以普通 Python 字符串的形式返回项目的 URL。和 get_absolute_url() 一样,item_link() 的结果将直接包含在 URL 中,所以你要负责在方法本身内部进行所有必要的 URL 引用和 ASCII 转换。

一个复杂的例子

该框架还支持更复杂的 feed,通过参数。

例如,一个网站可以为一个城市的每一个警察局提供最近发生的犯罪行为的 RSS feed。如果为每个警察巡逻创建一个单独的 Feed 类,那就太傻了;那会违反 DRY 原则,并且会把数据和编程逻辑联系起来。相反,聚合框架让你访问从你的 URLconf 传递的参数,这样 feed 就可以根据 URL 中的信息输出项目。

可以通过这样的网址访问警察局 feed:

  • /beats/613/rss/ — Returns recent crimes for beat 613.
  • /beats/1424/rss/ — Returns recent crimes for beat 1424.

这些可以用 URLconf 行来匹配,如:

  1. path("beats/<int:beat_id>/rss/", BeatFeed()),

像视图一样,URL 中的参数和请求对象一起传递给 get_object() 方法。

以下是这些特定警察局 feed 的代码:

  1. from django.contrib.syndication.views import Feed
  2. class BeatFeed(Feed):
  3. description_template = "feeds/beat_description.html"
  4. def get_object(self, request, beat_id):
  5. return Beat.objects.get(pk=beat_id)
  6. def title(self, obj):
  7. return "Police beat central: Crimes for beat %s" % obj.beat
  8. def link(self, obj):
  9. return obj.get_absolute_url()
  10. def description(self, obj):
  11. return "Crimes recently reported in police beat %s" % obj.beat
  12. def items(self, obj):
  13. return Crime.objects.filter(beat=obj).order_by("-crime_date")[:30]

为了生成 feed 的 <title><link><description>,Django 使用了 title()link()description() 方法。在前面的例子中,它们是字符串类属性,但这个例子说明它们可以是字符串 方法。对于每一个 titlelink 和``description``,Django 都遵循这个算法。

  • 首先,它试图调用一个方法,传递 obj 参数,其中 objget_object() 返回的对象。
  • 如果失败了,它就会尝试调用一个没有参数的方法。
  • 如果失败了,它就会使用类属性。

还请注意,items() 也遵循同样的算法——首先,它尝试 items(obj),然后是 items(),最后是 items 类属性(应该是一个列表)。

我们是用一个模板来描述项目。可以是这样的最小化:

  1. {{ obj.description }}

但是,你可以根据需要自由添加格式。

下面的 ExampleFeed 类给出了关于 Feed 类的方法和属性的完整文档。

指定 feed 的类型

默认情况下,这个框架中产生的 feed 使用 RSS 2.0。

要改变这一点,可以在你的 Feed 类中添加一个 feed_type 属性,像这样:

  1. from django.utils.feedgenerator import Atom1Feed
  2. class MyFeed(Feed):
  3. feed_type = Atom1Feed

请注意,你把 feed_type 设置为一个类对象,而不是一个实例。

目前可用的 feed 类型有:

封面

要指定封面,如创建播客 feed 时使用的封面,使用 item_enclosures 钩子,或者,如果每个项目只有一个封面,则使用 item_enclosure_urlitem_enclosure_lengthitem_enclosure_mime_type` 钩子。请看下面的 ExampleFeed 类的使用示例。

语言

由聚合框架创建的 Feed 会自动包含相应的 <language> 标签(RSS 2.0)或 xml:lang 属性(Atom)。默认情况下,这是 django.utils.translation.get_language()。你可以通过设置 language 类属性来改变它。

URL

The link method/attribute can return either an absolute path (e.g. "/blog/") or a URL with the fully-qualified domain and protocol (e.g. "https://www.example.com/blog/"). If link doesn’t return the domain, the syndication framework will insert the domain of the current site, according to your SITE_ID setting.

Atom feeds 需要一个 <link rel="self">,定义 feed 的当前位置。聚合框架会根据 SITE_ID 设置,使用当前网站的域名自动填充。

同时发布 Atom 和 RSS feed

有些开发者喜欢同时提供 Atom 和 RSS 版本的 feed。要做到这一点,你可以创建一个 Feed 类的子类,并将 feed_type 设置为不同的内容。然后更新你的 URLconf 来添加额外的版本。

这有一个完整的例子:

  1. from django.contrib.syndication.views import Feed
  2. from policebeat.models import NewsItem
  3. from django.utils.feedgenerator import Atom1Feed
  4. class RssSiteNewsFeed(Feed):
  5. title = "Police beat site news"
  6. link = "/sitenews/"
  7. description = "Updates on changes and additions to police beat central."
  8. def items(self):
  9. return NewsItem.objects.order_by("-pub_date")[:5]
  10. class AtomSiteNewsFeed(RssSiteNewsFeed):
  11. feed_type = Atom1Feed
  12. subtitle = RssSiteNewsFeed.description

备注

在这个例子中,RSS feed使用 description,而 Atom feed 使用 subtitle。这是因为 Atom feed 不提供 feed 级别的“描述”,但它们 提供 “副标题”。

如果你在你的 Feed 类中提供了一个 description,Django 不会自动将其放入 subtitle 元素中,因为副标题和描述不一定是一回事。相反,你应该定义一个 subtitle 属性。

在上面的例子中,我们将 Atom feed 的 subtitle 设置为 RSS feed 的 description,因为它已经很短了。

并附带 URLconf:

  1. from django.urls import path
  2. from myproject.feeds import AtomSiteNewsFeed, RssSiteNewsFeed
  3. urlpatterns = [
  4. # ...
  5. path("sitenews/rss/", RssSiteNewsFeed()),
  6. path("sitenews/atom/", AtomSiteNewsFeed()),
  7. # ...
  8. ]

Feed 类参考

class views.Feed

这个例子说明了 Feed 类的所有可能的属性和方法:

  1. from django.contrib.syndication.views import Feed
  2. from django.utils import feedgenerator
  3. class ExampleFeed(Feed):
  4. # FEED TYPE -- Optional. This should be a class that subclasses
  5. # django.utils.feedgenerator.SyndicationFeed. This designates
  6. # which type of feed this should be: RSS 2.0, Atom 1.0, etc. If
  7. # you don't specify feed_type, your feed will be RSS 2.0. This
  8. # should be a class, not an instance of the class.
  9. feed_type = feedgenerator.Rss201rev2Feed
  10. # TEMPLATE NAMES -- Optional. These should be strings
  11. # representing names of Django templates that the system should
  12. # use in rendering the title and description of your feed items.
  13. # Both are optional. If a template is not specified, the
  14. # item_title() or item_description() methods are used instead.
  15. title_template = None
  16. description_template = None
  17. # LANGUAGE -- Optional. This should be a string specifying a language
  18. # code. Defaults to django.utils.translation.get_language().
  19. language = "de"
  20. # TITLE -- One of the following three is required. The framework
  21. # looks for them in this order.
  22. def title(self, obj):
  23. """
  24. Takes the object returned by get_object() and returns the
  25. feed's title as a normal Python string.
  26. """
  27. def title(self):
  28. """
  29. Returns the feed's title as a normal Python string.
  30. """
  31. title = "foo" # Hard-coded title.
  32. # LINK -- One of the following three is required. The framework
  33. # looks for them in this order.
  34. def link(self, obj):
  35. """
  36. # Takes the object returned by get_object() and returns the URL
  37. # of the HTML version of the feed as a normal Python string.
  38. """
  39. def link(self):
  40. """
  41. Returns the URL of the HTML version of the feed as a normal Python
  42. string.
  43. """
  44. link = "/blog/" # Hard-coded URL.
  45. # FEED_URL -- One of the following three is optional. The framework
  46. # looks for them in this order.
  47. def feed_url(self, obj):
  48. """
  49. # Takes the object returned by get_object() and returns the feed's
  50. # own URL as a normal Python string.
  51. """
  52. def feed_url(self):
  53. """
  54. Returns the feed's own URL as a normal Python string.
  55. """
  56. feed_url = "/blog/rss/" # Hard-coded URL.
  57. # GUID -- One of the following three is optional. The framework looks
  58. # for them in this order. This property is only used for Atom feeds
  59. # (where it is the feed-level ID element). If not provided, the feed
  60. # link is used as the ID.
  61. def feed_guid(self, obj):
  62. """
  63. Takes the object returned by get_object() and returns the globally
  64. unique ID for the feed as a normal Python string.
  65. """
  66. def feed_guid(self):
  67. """
  68. Returns the feed's globally unique ID as a normal Python string.
  69. """
  70. feed_guid = "/foo/bar/1234" # Hard-coded guid.
  71. # DESCRIPTION -- One of the following three is required. The framework
  72. # looks for them in this order.
  73. def description(self, obj):
  74. """
  75. Takes the object returned by get_object() and returns the feed's
  76. description as a normal Python string.
  77. """
  78. def description(self):
  79. """
  80. Returns the feed's description as a normal Python string.
  81. """
  82. description = "Foo bar baz." # Hard-coded description.
  83. # AUTHOR NAME --One of the following three is optional. The framework
  84. # looks for them in this order.
  85. def author_name(self, obj):
  86. """
  87. Takes the object returned by get_object() and returns the feed's
  88. author's name as a normal Python string.
  89. """
  90. def author_name(self):
  91. """
  92. Returns the feed's author's name as a normal Python string.
  93. """
  94. author_name = "Sally Smith" # Hard-coded author name.
  95. # AUTHOR EMAIL --One of the following three is optional. The framework
  96. # looks for them in this order.
  97. def author_email(self, obj):
  98. """
  99. Takes the object returned by get_object() and returns the feed's
  100. author's email as a normal Python string.
  101. """
  102. def author_email(self):
  103. """
  104. Returns the feed's author's email as a normal Python string.
  105. """
  106. author_email = "test@example.com" # Hard-coded author email.
  107. # AUTHOR LINK --One of the following three is optional. The framework
  108. # looks for them in this order. In each case, the URL should include
  109. # the "http://" and domain name.
  110. def author_link(self, obj):
  111. """
  112. Takes the object returned by get_object() and returns the feed's
  113. author's URL as a normal Python string.
  114. """
  115. def author_link(self):
  116. """
  117. Returns the feed's author's URL as a normal Python string.
  118. """
  119. author_link = "https://www.example.com/" # Hard-coded author URL.
  120. # CATEGORIES -- One of the following three is optional. The framework
  121. # looks for them in this order. In each case, the method/attribute
  122. # should return an iterable object that returns strings.
  123. def categories(self, obj):
  124. """
  125. Takes the object returned by get_object() and returns the feed's
  126. categories as iterable over strings.
  127. """
  128. def categories(self):
  129. """
  130. Returns the feed's categories as iterable over strings.
  131. """
  132. categories = ["python", "django"] # Hard-coded list of categories.
  133. # COPYRIGHT NOTICE -- One of the following three is optional. The
  134. # framework looks for them in this order.
  135. def feed_copyright(self, obj):
  136. """
  137. Takes the object returned by get_object() and returns the feed's
  138. copyright notice as a normal Python string.
  139. """
  140. def feed_copyright(self):
  141. """
  142. Returns the feed's copyright notice as a normal Python string.
  143. """
  144. feed_copyright = "Copyright (c) 2007, Sally Smith" # Hard-coded copyright notice.
  145. # TTL -- One of the following three is optional. The framework looks
  146. # for them in this order. Ignored for Atom feeds.
  147. def ttl(self, obj):
  148. """
  149. Takes the object returned by get_object() and returns the feed's
  150. TTL (Time To Live) as a normal Python string.
  151. """
  152. def ttl(self):
  153. """
  154. Returns the feed's TTL as a normal Python string.
  155. """
  156. ttl = 600 # Hard-coded Time To Live.
  157. # ITEMS -- One of the following three is required. The framework looks
  158. # for them in this order.
  159. def items(self, obj):
  160. """
  161. Takes the object returned by get_object() and returns a list of
  162. items to publish in this feed.
  163. """
  164. def items(self):
  165. """
  166. Returns a list of items to publish in this feed.
  167. """
  168. items = ["Item 1", "Item 2"] # Hard-coded items.
  169. # GET_OBJECT -- This is required for feeds that publish different data
  170. # for different URL parameters. (See "A complex example" above.)
  171. def get_object(self, request, *args, **kwargs):
  172. """
  173. Takes the current request and the arguments from the URL, and
  174. returns an object represented by this feed. Raises
  175. django.core.exceptions.ObjectDoesNotExist on error.
  176. """
  177. # ITEM TITLE AND DESCRIPTION -- If title_template or
  178. # description_template are not defined, these are used instead. Both are
  179. # optional, by default they will use the string representation of the
  180. # item.
  181. def item_title(self, item):
  182. """
  183. Takes an item, as returned by items(), and returns the item's
  184. title as a normal Python string.
  185. """
  186. def item_title(self):
  187. """
  188. Returns the title for every item in the feed.
  189. """
  190. item_title = "Breaking News: Nothing Happening" # Hard-coded title.
  191. def item_description(self, item):
  192. """
  193. Takes an item, as returned by items(), and returns the item's
  194. description as a normal Python string.
  195. """
  196. def item_description(self):
  197. """
  198. Returns the description for every item in the feed.
  199. """
  200. item_description = "A description of the item." # Hard-coded description.
  201. def get_context_data(self, **kwargs):
  202. """
  203. Returns a dictionary to use as extra context if either
  204. description_template or item_template are used.
  205. Default implementation preserves the old behavior
  206. of using {'obj': item, 'site': current_site} as the context.
  207. """
  208. # ITEM LINK -- One of these three is required. The framework looks for
  209. # them in this order.
  210. # First, the framework tries the two methods below, in
  211. # order. Failing that, it falls back to the get_absolute_url()
  212. # method on each item returned by items().
  213. def item_link(self, item):
  214. """
  215. Takes an item, as returned by items(), and returns the item's URL.
  216. """
  217. def item_link(self):
  218. """
  219. Returns the URL for every item in the feed.
  220. """
  221. # ITEM_GUID -- The following method is optional. If not provided, the
  222. # item's link is used by default.
  223. def item_guid(self, obj):
  224. """
  225. Takes an item, as return by items(), and returns the item's ID.
  226. """
  227. # ITEM_GUID_IS_PERMALINK -- The following method is optional. If
  228. # provided, it sets the 'isPermaLink' attribute of an item's
  229. # GUID element. This method is used only when 'item_guid' is
  230. # specified.
  231. def item_guid_is_permalink(self, obj):
  232. """
  233. Takes an item, as returned by items(), and returns a boolean.
  234. """
  235. item_guid_is_permalink = False # Hard coded value
  236. # ITEM AUTHOR NAME -- One of the following three is optional. The
  237. # framework looks for them in this order.
  238. def item_author_name(self, item):
  239. """
  240. Takes an item, as returned by items(), and returns the item's
  241. author's name as a normal Python string.
  242. """
  243. def item_author_name(self):
  244. """
  245. Returns the author name for every item in the feed.
  246. """
  247. item_author_name = "Sally Smith" # Hard-coded author name.
  248. # ITEM AUTHOR EMAIL --One of the following three is optional. The
  249. # framework looks for them in this order.
  250. #
  251. # If you specify this, you must specify item_author_name.
  252. def item_author_email(self, obj):
  253. """
  254. Takes an item, as returned by items(), and returns the item's
  255. author's email as a normal Python string.
  256. """
  257. def item_author_email(self):
  258. """
  259. Returns the author email for every item in the feed.
  260. """
  261. item_author_email = "test@example.com" # Hard-coded author email.
  262. # ITEM AUTHOR LINK -- One of the following three is optional. The
  263. # framework looks for them in this order. In each case, the URL should
  264. # include the "http://" and domain name.
  265. #
  266. # If you specify this, you must specify item_author_name.
  267. def item_author_link(self, obj):
  268. """
  269. Takes an item, as returned by items(), and returns the item's
  270. author's URL as a normal Python string.
  271. """
  272. def item_author_link(self):
  273. """
  274. Returns the author URL for every item in the feed.
  275. """
  276. item_author_link = "https://www.example.com/" # Hard-coded author URL.
  277. # ITEM ENCLOSURES -- One of the following three is optional. The
  278. # framework looks for them in this order. If one of them is defined,
  279. # ``item_enclosure_url``, ``item_enclosure_length``, and
  280. # ``item_enclosure_mime_type`` will have no effect.
  281. def item_enclosures(self, item):
  282. """
  283. Takes an item, as returned by items(), and returns a list of
  284. ``django.utils.feedgenerator.Enclosure`` objects.
  285. """
  286. def item_enclosures(self):
  287. """
  288. Returns the ``django.utils.feedgenerator.Enclosure`` list for every
  289. item in the feed.
  290. """
  291. item_enclosures = [] # Hard-coded enclosure list
  292. # ITEM ENCLOSURE URL -- One of these three is required if you're
  293. # publishing enclosures and you're not using ``item_enclosures``. The
  294. # framework looks for them in this order.
  295. def item_enclosure_url(self, item):
  296. """
  297. Takes an item, as returned by items(), and returns the item's
  298. enclosure URL.
  299. """
  300. def item_enclosure_url(self):
  301. """
  302. Returns the enclosure URL for every item in the feed.
  303. """
  304. item_enclosure_url = "/foo/bar.mp3" # Hard-coded enclosure link.
  305. # ITEM ENCLOSURE LENGTH -- One of these three is required if you're
  306. # publishing enclosures and you're not using ``item_enclosures``. The
  307. # framework looks for them in this order. In each case, the returned
  308. # value should be either an integer, or a string representation of the
  309. # integer, in bytes.
  310. def item_enclosure_length(self, item):
  311. """
  312. Takes an item, as returned by items(), and returns the item's
  313. enclosure length.
  314. """
  315. def item_enclosure_length(self):
  316. """
  317. Returns the enclosure length for every item in the feed.
  318. """
  319. item_enclosure_length = 32000 # Hard-coded enclosure length.
  320. # ITEM ENCLOSURE MIME TYPE -- One of these three is required if you're
  321. # publishing enclosures and you're not using ``item_enclosures``. The
  322. # framework looks for them in this order.
  323. def item_enclosure_mime_type(self, item):
  324. """
  325. Takes an item, as returned by items(), and returns the item's
  326. enclosure MIME type.
  327. """
  328. def item_enclosure_mime_type(self):
  329. """
  330. Returns the enclosure MIME type for every item in the feed.
  331. """
  332. item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
  333. # ITEM PUBDATE -- It's optional to use one of these three. This is a
  334. # hook that specifies how to get the pubdate for a given item.
  335. # In each case, the method/attribute should return a Python
  336. # datetime.datetime object.
  337. def item_pubdate(self, item):
  338. """
  339. Takes an item, as returned by items(), and returns the item's
  340. pubdate.
  341. """
  342. def item_pubdate(self):
  343. """
  344. Returns the pubdate for every item in the feed.
  345. """
  346. item_pubdate = datetime.datetime(2005, 5, 3) # Hard-coded pubdate.
  347. # ITEM UPDATED -- It's optional to use one of these three. This is a
  348. # hook that specifies how to get the updateddate for a given item.
  349. # In each case, the method/attribute should return a Python
  350. # datetime.datetime object.
  351. def item_updateddate(self, item):
  352. """
  353. Takes an item, as returned by items(), and returns the item's
  354. updateddate.
  355. """
  356. def item_updateddate(self):
  357. """
  358. Returns the updateddate for every item in the feed.
  359. """
  360. item_updateddate = datetime.datetime(2005, 5, 3) # Hard-coded updateddate.
  361. # ITEM CATEGORIES -- It's optional to use one of these three. This is
  362. # a hook that specifies how to get the list of categories for a given
  363. # item. In each case, the method/attribute should return an iterable
  364. # object that returns strings.
  365. def item_categories(self, item):
  366. """
  367. Takes an item, as returned by items(), and returns the item's
  368. categories.
  369. """
  370. def item_categories(self):
  371. """
  372. Returns the categories for every item in the feed.
  373. """
  374. item_categories = ["python", "django"] # Hard-coded categories.
  375. # ITEM COPYRIGHT NOTICE (only applicable to Atom feeds) -- One of the
  376. # following three is optional. The framework looks for them in this
  377. # order.
  378. def item_copyright(self, obj):
  379. """
  380. Takes an item, as returned by items(), and returns the item's
  381. copyright notice as a normal Python string.
  382. """
  383. def item_copyright(self):
  384. """
  385. Returns the copyright notice for every item in the feed.
  386. """
  387. item_copyright = "Copyright (c) 2007, Sally Smith" # Hard-coded copyright notice.
  388. # ITEM COMMENTS URL -- It's optional to use one of these three. This is
  389. # a hook that specifies how to get the URL of a page for comments for a
  390. # given item.
  391. def item_comments(self, obj):
  392. """
  393. Takes an item, as returned by items(), and returns the item's
  394. comments URL as a normal Python string.
  395. """
  396. def item_comments(self):
  397. """
  398. Returns the comments URL for every item in the feed.
  399. """
  400. item_comments = "https://www.example.com/comments" # Hard-coded comments URL

低级框架

在幕后,高级 RSS 框架使用一个低级框架来生成 feed 的 XML。这个框架存在于一个模块中:django/utils/feedgenerator.py

你可以自己使用这个框架,进行低级的 feed 生成。你也可以创建自定义的 feed 生成器子类,用于 feed_type Feed 选项。

SyndicationFeed

feedgenerator 模块包含一个基类:

和几个子类:

这三个类中的每一个都知道如何将某种类型的 feed 渲染成 XML。它们共享这个接口:

SyndicationFeed.__init__()

用给定的元数据字典初始化 feed,该字典适用于整个 feed。所需的关键字参数有:

  • title
  • link
  • description

还有一堆其他可选的关键词:

  • language
  • author_email
  • author_name
  • author_link
  • subtitle
  • categories
  • feed_url
  • feed_copyright
  • feed_guid
  • ttl

你传递给 __init__ 的任何额外的关键字参数将被存储在 self.feed 中,供 自定义 feed 生成器 使用。

所有参数都应该是字符串,但 categories 除外,它应该是一个字符串序列。请注意,在 XML 文档中,有些控制字符是 不允许的 。如果你的内容中有一些,你可能会在生成 feed 时遇到一个 ValueError

SyndicationFeed.add_item()

用给定的参数将一个项目添加到 feed 中。

必须的关键字参数是:

  • title
  • link
  • description

可选的关键字参数是:

  • author_email
  • author_name
  • author_link
  • pubdate
  • comments
  • unique_id
  • enclosures
  • categories
  • item_copyright
  • ttl
  • updateddate

额外的关键字参数将被存储在 自定义 feed 生成器

所有参数,如果给定,都应该是字符串,除了:

  • pubdate 应该是一个 Python datetime 对象。
  • updateddate 应该是一个 Python datetime 对象。
  • enclosures 应该是一个 django.utils.feedgenerator.Eclosure 实例的列表。
  • categories 应是一个字符串序列。

SyndicationFeed.write()

将给定编码的 feed 输出到 outfile,一个类似文件的对象。

SyndicationFeed.writeString()

以给定编码的字符串形式返回 feed。

For example, to create an Atom 1.0 feed and print it to standard output:

  1. >>> from django.utils import feedgenerator
  2. >>> from datetime import datetime
  3. >>> f = feedgenerator.Atom1Feed(
  4. ... title="My Blog",
  5. ... link="https://www.example.com/",
  6. ... description="In which I write about what I ate today.",
  7. ... language="en",
  8. ... author_name="Myself",
  9. ... feed_url="https://example.com/atom.xml",
  10. ... )
  11. >>> f.add_item(
  12. ... title="Hot dog today",
  13. ... link="https://www.example.com/entries/1/",
  14. ... pubdate=datetime.now(),
  15. ... description="<p>Today I had a Vienna Beef hot dog. It was pink, plump and perfect.</p>",
  16. ... )
  17. >>> print(f.writeString("UTF-8"))
  18. <?xml version="1.0" encoding="UTF-8"?>
  19. <feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  20. ...
  21. </feed>

自定义 feed 生成器

如果你需要制作一个自定义的 feed 格式,你有几个选择。

如果 feed 格式是完全自定义的,你会想要子类化 SyndicationFeed 并完全替换 write()writeString() 方法。

然而,如果 feed 格式是 RSS 或 Atom 的衍生产品(即 GeoRSS ,苹果的 iTunes podcast format 等),你就有了更好的选择。这些类型的 feeds 通常会向底层格式添加额外的元素和/或属性,而且 SyndicationFeed 调用一组方法来获取这些额外的属性。因此,你可以子类化适当的 feed 生成器类(Atom1FeedRss201rev2Feed)并扩展这些回调。它们是:

SyndicationFeed.root_attributes(self)

返回一个属性的 dict 以添加到根 feed 元素(feedchannel)。

SyndicationFeed.add_root_elements(self, handler)

回调以在根 feed 元素(feedchannel)中添加元素。handler 是一个 XMLGenerator,来自 Python 内置的 SAX 库;你将调用它上的方法来添加到正在处理的 XML 文档中。

SyndicationFeed.item_attributes(self, item)

返回要添加到每个项目(itementry)元素的 dict 属性。参数 item 是传递给 SyndicationFeed.add_item() 的所有数据的字典。

SyndicationFeed.add_item_elements(self, handler, item)

回调为每个项目(itementry)元素添加元素。handleritem 同上。

警告

如果你覆盖了这些方法中的任何一个,一定要调用超级类方法,因为它们为每种 feed 格式添加了所需的元素。

例如,你可以这样开始实现一个 iTunes RSS feed 生成器:

  1. class iTunesFeed(Rss201rev2Feed):
  2. def root_attributes(self):
  3. attrs = super().root_attributes()
  4. attrs["xmlns:itunes"] = "http://www.itunes.com/dtds/podcast-1.0.dtd"
  5. return attrs
  6. def add_root_elements(self, handler):
  7. super().add_root_elements(handler)
  8. handler.addQuickElement("itunes:explicit", "clean")

对于一个完整的自定义 feed 类来说,还有很多工作要做,但上面的例子应该展示了基本的想法。