Feed 聚合框架

Feed 聚合框架

Django 提供了一个高级的 feed 聚合生成框架来创建 RSS 和 Atom 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:

from django.contrib.syndication.views import Feed
from django.urls 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])

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

from django.urls import path
from myproject.feeds import LatestEntriesFeed

urlpatterns = [
    # ...
    path("latest/feed/", LatestEntriesFeed()),
    # ...
]

注意:

  • Feed 类的子类 django.contrib.syndication.views.Feed。
  • title、link 和 description 分别对应标准的 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_template 和 description_template 属性来指定。每个项目的模板都会被渲染,并传递两个模板上下文变量。

* `{{ obj }}`——当前对象(你在 `items()` 中返回的对象之一)。
* `{{ site }}` —— 代表当前站点的 [django.contrib.sites.models.Site](&lt;/read/django-5.0-zh/4a464892bf9f10c4.md#django.contrib.sites.models.Site> "django.contrib.sites.models.Site") 对象。这对于 `{ site.domain }}` 或 `{ site.name }}` 是很有用的。如果你没有安装 Django 站点框架,那么这个对象将被设置为一个 [RequestSite](&lt;/read/django-5.0-zh/4a464892bf9f10c4.md#django.contrib.sites.requests.RequestSite> "django.contrib.sites.requests.RequestSite") 对象。更多内容请参考 [网站框架文档中的 RequestSite 部分](/read/django-5.0-zh/d180.md#id3)。

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

Feed.get_context_data(**kwargs)

如果你需要提供比前面提到的两个变量更多的信息,还有一种方法可以向标题和描述模板传递额外的信息。你可以在你的 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().get_context_data(**kwargs)
        context["foo"] = "bar"
        return context

还有模板:

Something about {{ foo }}: {{ obj.description }}

本方法将对 items() 返回的列表中的每个项目调用一次,其关键字参数如下:

  • item:当前项目。出于向后兼容的原因,这个上下文变量的名称是 {{ obj }}。
  • obj:由 get_object() 返回的对象。默认情况下,它不会暴露在模板中,以避免与 {{ obj }} 混淆(见上文),但你可以在实现 get_context_data() 时使用它。
  • site:如上所述的当前站点。
  • request:当前请求。

get_context_data() 的行为模仿 通用视图——你应该调用 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 原则](/read/django-5.0-zh/d234.md#dry),并且会把数据和编程逻辑联系起来。相反,聚合框架让你访问从你的 [URLconf](/read/django-5.0-zh/d035.md) 传递的参数,这样 feed 就可以根据 URL 中的信息输出项目。

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

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

这些可以用 [URLconf](/read/django-5.0-zh/d035.md) 行来匹配,如:

path("beats//rss/", BeatFeed()),


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

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

from django.contrib.syndication.views import Feed

class BeatFeed(Feed): description_template = "feeds/beat_description.html"

def get_object(self, request, beat_id):
    return Beat.objects.get(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]

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

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

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

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

{{ obj.description }}


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

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

### 指定 feed 的类型

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

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

from django.utils.feedgenerator import Atom1Feed

class MyFeed(Feed): feed_type = Atom1Feed


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

目前可用的 feed 类型有:

  * [django.utils.feedgenerator.Rss201rev2Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.Rss201rev2Feed> "django.utils.feedgenerator.Rss201rev2Feed") (RSS 2.01。默认值。)
  * [django.utils.feedgenerator.RssUserland091Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.RssUserland091Feed> "django.utils.feedgenerator.RssUserland091Feed") (RSS 0.91。)
  * [django.utils.feedgenerator.Atom1Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.Atom1Feed> "django.utils.feedgenerator.Atom1Feed") (Atom 1.0。)

### 封面

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

### 语言

由聚合框架创建的 Feed 会自动包含相应的 `<language>` 标签(RSS 2.0)或 `xml:lang` 属性(Atom)。默认情况下,这是 [django.utils.translation.get_language()](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.translation.get_language> "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](/read/django-5.0-zh/d219.md#std-setting-SITE_ID).

Atom feeds 需要一个 `<link rel="self">`,定义 feed 的当前位置。聚合框架会根据 [SITE_ID](/read/django-5.0-zh/d219.md#std-setting-SITE_ID) 设置,使用当前网站的域名自动填充。

### 同时发布 Atom 和 RSS feed

有些开发者喜欢同时提供 Atom 和 RSS 版本的 feed。要做到这一点,你可以创建一个 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 feed使用 `description`,而 Atom feed 使用 `subtitle`。这是因为 Atom feed 不提供 feed 级别的“描述”,但它们 _提供_ “副标题”。

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

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

并附带 URLconf:

from django.urls import path from myproject.feeds import AtomSiteNewsFeed, RssSiteNewsFeed

urlpatterns = [ # ... path("sitenews/rss/", RssSiteNewsFeed()), path("sitenews/atom/", AtomSiteNewsFeed()), # ... ]


### `Feed` 类参考

 _class_ `views.Feed`

这个例子说明了 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

# LANGUAGE -- Optional. This should be a string specifying a language
# code. Defaults to django.utils.translation.get_language().
language = "de"

# 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 = "test@example.com"  # 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 = "https://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 string 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 = "test@example.com"  # 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 = "https://www.example.com/"  # Hard-coded author URL.

# ITEM ENCLOSURES -- One of the following three is optional. The
# framework looks for them in this order. If one of them is defined,
# ``item_enclosure_url``, ``item_enclosure_length``, and
# ``item_enclosure_mime_type`` will have no effect.

def item_enclosures(self, item):
    """
    Takes an item, as returned by items(), and returns a list of
    ``django.utils.feedgenerator.Enclosure`` objects.
    """

def item_enclosures(self):
    """
    Returns the ``django.utils.feedgenerator.Enclosure`` list for every
    item in the feed.
    """

item_enclosures = []  # Hard-coded enclosure list

# ITEM ENCLOSURE URL -- One of these three is required if you're
# publishing enclosures and you're not using ``item_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 and you're not using ``item_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 and you're not using ``item_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 updateddate 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.

# ITEM COMMENTS URL -- It's optional to use one of these three. This is
# a hook that specifies how to get the URL of a page for comments for a
# given item.

def item_comments(self, obj):
    """
    Takes an item, as returned by items(), and returns the item's
    comments URL as a normal Python string.
    """

def item_comments(self):
    """
    Returns the comments URL for every item in the feed.
    """

item_comments = "https://www.example.com/comments"  # Hard-coded comments URL

## 低级框架

在幕后,高级 RSS 框架使用一个低级框架来生成 feed 的 XML。这个框架存在于一个模块中:[django/utils/feedgenerator.py](&lt;https://github.com/django/django/blob/main/django/utils/feedgenerator.py>)。

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

### `SyndicationFeed` 类

[feedgenerator](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#module-django.utils.feedgenerator> "django.utils.feedgenerator: Syndication feed generation library -- used for generating RSS, etc.") 模块包含一个基类:

  * [django.utils.feedgenerator.SyndicationFeed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.SyndicationFeed> "django.utils.feedgenerator.SyndicationFeed")

和几个子类:

  * [django.utils.feedgenerator.RssUserland091Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.RssUserland091Feed> "django.utils.feedgenerator.RssUserland091Feed")
  * [django.utils.feedgenerator.Rss201rev2Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.Rss201rev2Feed> "django.utils.feedgenerator.Rss201rev2Feed")
  * [django.utils.feedgenerator.Atom1Feed](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.Atom1Feed> "django.utils.feedgenerator.Atom1Feed")

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

[SyndicationFeed.__init__()](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.SyndicationFeed.__init__> "django.utils.feedgenerator.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 文档中,有些控制字符是 [不允许的](&lt;https://www.w3.org/International/questions/qa-controls>) 。如果你的内容中有一些,你可能会在生成 feed 时遇到一个 [ValueError](&lt;https://docs.python.org/3/library/exceptions.html#ValueError> "\(在 Python v3.12\)")。

[SyndicationFeed.add_item()](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.SyndicationFeed.add_item> "django.utils.feedgenerator.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](&lt;https://docs.python.org/3/library/datetime.html#datetime.datetime> "\(在 Python v3.12\)") 对象。
  * `updateddate` 应该是一个 Python [datetime](&lt;https://docs.python.org/3/library/datetime.html#datetime.datetime> "\(在 Python v3.12\)") 对象。
  * `enclosures` 应该是一个 `django.utils.feedgenerator.Eclosure` 实例的列表。
  * `categories` 应是一个字符串序列。

[SyndicationFeed.write()](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.SyndicationFeed.write> "django.utils.feedgenerator.SyndicationFeed.write")

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

[SyndicationFeed.writeString()](&lt;/read/django-5.0-zh/9e94346b83a7c66c.md#django.utils.feedgenerator.SyndicationFeed.writeString> "django.utils.feedgenerator.SyndicationFeed.writeString")

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

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

from django.utils import feedgenerator from datetime import datetime f = feedgenerator.Atom1Feed( ... title="My Blog", ... link="https://www.example.com/", ... description="In which I write about what I ate today.", ... language="en", ... author_name="Myself", ... feed_url="https://example.com/atom.xml", ... ) f.add_item( ... title="Hot dog today", ... link="https://www.example.com/entries/1/", ... pubdate=datetime.now(), ... description="

Today I had a Vienna Beef hot dog. It was pink, plump and perfect.

", ... ) print(f.writeString("UTF-8"))

...


### 自定义 feed 生成器

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

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

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

`SyndicationFeed.root_attributes(self)`

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

`SyndicationFeed.add_root_elements(self, handler)`

回调以在根 feed 元素(`feed`/`channel`)中添加元素。`handler` 是一个 [XMLGenerator](&lt;https://docs.python.org/3/library/xml.sax.utils.html#xml.sax.saxutils.XMLGenerator> "\(在 Python v3.12\)"),来自 Python 内置的 SAX 库;你将调用它上的方法来添加到正在处理的 XML 文档中。

`SyndicationFeed.item_attributes(self, item)`

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

`SyndicationFeed.add_item_elements(self, handler, item)`

回调为每个项目(`item`/`entry`)元素添加元素。`handler` 和 `item` 同上。

警告

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

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

class iTunesFeed(Rss201rev2Feed): def root_attributes(self): attrs = super().root_attributes() attrs["xmlns:itunes"] = "http://www.itunes.com/dtds/podcast-1.0.dtd" return attrs

def add_root_elements(self, handler):
    super().add_root_elements(handler)
    handler.addQuickElement("itunes:explicit", "clean")

```

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