Syndication Feed框架

2018-03-06  本文已影响40人  大爷的二舅

Django提供了一个高级联合供稿生成框架,可以轻松创建RSS和Atom供稿。 RSS和Atom都是基于XML的格式,可以用来自动更新网站内容的提要。 在这里阅读更多关于RSS的信息,并在这里获取有关Atom的信息。

要创建任何联合供稿,您只需编写一个简短的Python类。 您可以根据需要创建尽可能多的Feed。 Django还附带了一个较低级别的Feed生成API。 如果您想要在Web上下文之外生成订阅源,或者使用其他某种更低级的方式生成订阅源,请使用此选项。

高级框架

概观

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

Feed类

Feed类是一个表示联合供稿的Python类。 馈送可以是简单的(例如,网站新闻馈送或显示博客的最新条目的基本馈送)或更复杂(例如,显示特定类别中的所有博客条目的馈送,其中类别是可变的)。 Feed类的子类django.contrib.syndication.views.Feed。 他们可以生活在你的代码库的任何地方。 Feed类的实例是可以在你的URLconf中使用的视图。

一个简单的例子

这个简单的例子来自一个假设的警察新闻网站,描述了最新的五个新闻项目的饲料:

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,请将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代表用于两者。

如果您想为标题或描述进行特殊格式化,可以使用Django模板。 它们的路径可以通过Feed类中的title_template和description_template属性来指定。 为每个项目呈现模板并传递两个模板上下文变量:

请参阅下面使用说明模板的“复杂示例”。

如果您需要提供比前面提到的两个变量更多的信息,还可以将附加信息传递给标题和描述模板。 您可以在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 }}

这个方法将被item()返回的列表中的每个项目调用一次,其中包含以下关键字参数:

get_context_data()的行为模仿通用视图的行为 - 您应该调用super()从父类中检索上下文数据,添加数据并返回修改过的字典。

要指定<link>的内容,您有两个选项。 对于items()中的每个item,Django首先尝试调用Feed类中的item_link()方法。 以类似于标题和说明的方式,它传递了一个参数 - item。 如果该方法不存在,Django会尝试对该对象执行get_absolute_url()方法。

get_absolute_url()和item_link()都应该以正常的Python字符串的形式返回item的URL。 与get_absolute_url()一样,item_link()的结果将直接包含在URL中,因此您需要负责在方法本身内进行所有必要的URL引用和转换为ASCII。

一个复杂的例子

该框架还通过参数支持更复杂的提要。 例如,一个网站可以提供针对城市中每个警察的近期犯罪的RSS源。 为每个警察创建一个单独的饲料班是愚蠢的; 这会违反DRY原则并将数据耦合到编程逻辑。

相反,联合框架允许您访问从您的URLconf传递的参数,以便提要可以根据Feed的URL中的信息输出项目。 警察击败饲料可以通过这样的URL访问:

这些可以与URLconf行匹配,例如:

url(r'^beats/(?P[0-9]+)/rss/$', BeatFeed()),

与视图一样,URL中的参数与请求对象一起被传递给get_object()方法。 以下是这些特定于节拍的提要的代码:

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]

为了生成feed的<title>,<link>和<description>,Django使用title(),link()和description()方法。

在前面的例子中,它们是简单的字符串类属性,但这个例子说明它们可以是字符串或方法。 对于标题,链接和描述中的每一个,Django都遵循以下算法:

  1. 首先,它尝试调用一个方法,传递obj参数,其中obj是get_object()返回的对象。
  2. 否则,它会尝试调用一个没有参数的方法。
  3. 如果没有,它使用class属性。

还要注意,items()也遵循相同的算法 - 首先,它会尝试item(obj),然后item(),然后最后一个item类属性(应该是一个列表)。 我们正在使用项目描述的模板。 它可以非常简单:

   {{ 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类。

语言

由联合框架创建的源自动包含适当的<language>标记(RSS 2.0)或xml:lang属性(Atom)。这直接来自您的LANGUAGE_CODE设置。

网址

链接方法/属性可以返回绝对路径(例如/ blog /)或具有完全限定的域和协议的URL(例如http://www.example.com/blog/)。如果链接不返回域,联合框架将根据您的SITE_ID设置插入当前站点的域。 Atom提要需要<link rel =“self”>来定义提要的当前位置。联合框架根据SITE_ID设置自动填充当前网站的域。

串联发布Atom和RSS源

一些开发人员喜欢提供他们的提要的Atom和RSS版本。使用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提要使用描述,而Atom提要使用子标题。 这是因为Atom Feed不提供Feed级别的描述,但它们确实提供了子标题。 如果您在Feed类中提供了说明,则Django不会自动将其添加到子标题元素中,因为子标题和说明不一定是相同的东西。 相反,你应该定义一个子标题属性。

在上面的例子中,我们只需将Atom feeds字幕设置为RSS订阅源描述,因为它已经很短了。 和随附的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类的所有可能属性和方法的示例,请参阅供稿类参考

低级框架

在幕后,高层次的RSS框架使用较低层框架来生成提要的XML。 该框架位于单个模块中:django / utils / feedgenerator.py。 您可以自行使用此框架,以生成较低级别的Feed。 您还可以创建自定义Feed生成器子类以用于feed_type Feed选项。

SyndicationFeed类
feedgenerator模块包含一个基类:

和几个子类:

SyndicationFeed.init()

使用适用于整个Feed的元数据字典初始化Feed。 所需的关键字参数是:

还有其他一些可选的关键字:

您传递给init的任何额外关键字参数都将存储在self.feed中,以用于定制Feed生成器。 所有参数都应该是Unicode对象,但类别除外,它们应该是Unicode对象的序列。

SyndicationFeed.add_item()

使用给定参数向Feed添加item。

所需的关键字参数是:

可选的关键字参数是:

额外的关键字参数将被存储为自定义feed生成器。 所有的参数,如果给出,应该是Unicode对象,除了:

SyndicationFeed.write()

将给定编码中的馈送输出到outfile,这是一个类似文件的对象。

SyndicationFeed.writeString()

以给定编码中的字符串形式返回提要。 例如,要创建一个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 per\
fect.</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,Apple的iTunes播客格式等),则您有更好的选择。

这些类型的提要通常会为底层格式添加额外的元素和/或属性,并且SyndicationFeed会调用一组方法来获取这些额外的属性。因此,您可以继承相应的Feed生成器类(Atom1Feed或Rss201rev2Feed)并扩展这些回调。他们是:

SyndicationFeed.root_attributes(self,)

返回要添加到根提要素(提要/通道)的属性字典。

SyndicationFeed.add_root_elements(self,handler)

回调在根Feed元素(feed / channel)中添加元素。处理程序是Python内置SAX库的XMLGenerator;您将调用其中的方法来添加到正在处理的XML文档中。

SyndicationFeed.item_attributes(self,item)

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

SyndicationFeed.add_item_elements(self,handler,item)

回调添加元素到每个项目(item / entry)元素。处理程序和项目如上所述。

如果您重写了这些方法中的任何一个,请确保调用超类方法,因为它们为每种提要格式添加了必需的元素。

例如,您可能会开始实施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类还有很多工作要做,但上面的例子应该说明基本思想。

上一篇 下一篇

猜你喜欢

热点阅读