# Django 3.2.11.dev 文档

# Feed 聚合框架

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

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

Django 还提供了一个低级的 feed 生成 API。如果你想在 Web 上下文之外生成 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` 对象。这对于 `{ site.domain }}` 或 `{ site.name }}` 是很有用的。如果你没有安装 Django 站点框架，那么这个对象将被设置为一个 `RequestSite` 对象。更多内容请参考 网站框架文档中的 RequestSite 部分。

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

  `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 原则，并且会把数据和编程逻辑联系起来。相反，聚合框架让你访问从你的 URLconf 传递的参数，这样 feed 就可以根据 URL 中的信息输出项目。

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

- `/beats/613/rss/` —— 返回 613 警察局的最近犯罪情况。
- `/beats/1424/rss/` —— 返回 1424 警察局的最近犯罪情况。

这些可以用 URLconf 行来匹配，如：

    path('beats/<int:beat_id>/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` （RSS 2.01。默认值。）
- `django.utils.feedgenerator.RssUserland091Feed` （RSS 0.91。）
- `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()`。你可以通过设置 `language` 类属性来改变它。

### URL

`link` 方法／属性可以返回一个绝对路径（例如 `"/blog/"`），也可以返回一个带有完全限定的域名和协议的 URL（例如 `"https://www.example.com/blog/"`）。如果 `link` 没有返回域名，聚合框架会根据你的 `SITE_ID 配置`，插入当前网站的域名。

Atom feeds 需要一个 `<link rel="self">`，定义 feed 的当前位置。聚合框架会根据 `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

Changed in Django 3.2:

通过 `item_comments` 钩子增加了对每个 feed 项目的评论 URL 的支持。

## 低级框架

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

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

### `SyndicationFeed` 类

`feedgenerator` 模块包含一个基类：

- `django.utils.feedgenerator.SyndicationFeed`

和几个子类：

- `django.utils.feedgenerator.RssUserland091Feed`
- `django.utils.feedgenerator.Rss201rev2Feed`
- `django.utils.feedgenerator.Atom1Feed`

这三个类中的每一个都知道如何将某种类型的 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。

例如，要创建一个 Atom 1.0 feed 并将其打印成标准输出：

    >>> from django.utils import feedgenerator
    >>> from datetime import datetime
    >>> f = feedgenerator.Atom1Feed(
    ...     title="My Weblog",
    ...     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="<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 格式，你有几个选择。

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

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

`SyndicationFeed.root_attributes(self)`
  返回一个属性的 `dict` 以添加到根 feed 元素（`feed`／`channel`）。
`SyndicationFeed.add_root_elements(self, handler)`
  回调以在根 feed 元素（`feed`／`channel`）中添加元素。`handler` 是一个 `XMLGenerator`，来自 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 类来说，还有很多工作要做，但上面的例子应该展示了基本的想法。