开启Django博客的RSS功能的实现方法
作者:HelloGitHub-追梦人物
文中所涉及的示例代码,已同步更新到HelloGitHub-Team仓库
博客提供RSS订阅应该是标配,这样读者就可以通过一些聚合阅读工具订阅你的博客,时时查看是否有文章更新,而不必每次都跳转到博客上来查看。现在我们就来为博客添加RSS订阅功能。
RSS简介
RSS(ReallySimpleSyndication)是一种描述和同步网站内容的格式,它采用XML作为内容传递的格式。简单来说就是网站可以把内容包装成符合RSS标准的XML格式文档。一旦网站内容符合一个统一的规范,那么人们就可以开发一种读取这种规范化的XML文档的工具来聚合各大网站的内容。例如一个读者可能关注了很多的博客网站,如果这些博客网站都支持RSS订阅的话,他就只需要一个聚合阅读器订阅这些博客,就可以在聚合器工具里看到全部博客的更新内容,而不必再分别访问各个博客去看有没有内容更新了。更多RSS的介绍,可参考rss(简易信息聚合)。
使用DjangoFeed类
根据以上对RSS的介绍,我们可以发现关键的地方就是根据网站的内容生成规范化的XML文档,幸运的是,Django已经内置了一些生成这个文档的方法,下面就使用这些方法来创建RSS订阅文档。
首先我们在blog应用的根目录下(models.py所在目录)新建一个feeds.py文件以存放和RSS功能相关的代码。让后在feeds.py中写入如下代码:
blog/feeds.py
fromdjango.contrib.syndication.viewsimportFeed from.modelsimportPost classAllPostsRssFeed(Feed): #显示在聚合阅读器上的标题 title="HelloDjango-blog-tutorial" #通过聚合阅读器跳转到网站的地址 link="/" #显示在聚合阅读器上的描述信息 description="HelloDjango-blog-tutorial全部文章" #需要显示的内容条目 defitems(self): returnPost.objects.all() #聚合器中显示的内容条目的标题 defitem_title(self,item): return"[%s]%s"%(item.category,item.title) #聚合器中显示的内容条目的描述 defitem_description(self,item): returnitem.body_html
代码应该不难理解,主要就是指定要生成的XML文档内容。各个属性和方法的含义已在代码中注释,你只需把相关的内容替换成符合你博客的描述即可。
需要注意一点的是这个方法:
#聚合器中显示的内容条目的描述 defitem_description(self,item): returnitem.body_html
item是文章(Post模型的实例),聚合内容的描述我们返回了body_html属性的值。模型中原本使用body属性存储博客文章的内容,但是这些内容是以Markdown格式的,并非所有的聚合内容阅读器都支持Markdown格式的解析,因此我们返回的是已经解析后的HTML格式内容。
但Post模型原来并没有body_html这个属性的,那它是哪里来的呢?
回顾一下PostDetailView这个视图,我们在获取博客文章post的详细内容的视图中,使用markdown这个Python库在解析post.body这个属性的值。这个解析的过程其实在很多地方都可能用到,不应该写死在PostDetailView这个视图里,所以我们先将PostDetailView视图的get_object方法解析Markdown内容的过程封装为一个函数,暂且将其放在blog/models.py中。
blog/models.py
defgenerate_rich_content(value): md=markdown.Markdown( extensions=[ "markdown.extensions.extra", "markdown.extensions.codehilite", #记得在顶部引入TocExtension和slugify TocExtension(slugify=slugify), ] ) content=md.convert(value) m=re.search(r'\s* (.*)
\s*
解析过程和原来一样,只是将解析得到的HTML内容(content)和目录(toc)放在一个字典里返回。
然后,我们来给Post模型定义几个属性,以便方便地获取解析后的内容和目录:
blog/models.py
fromdjango.utils.functionalimportcached_property classPost(models.Model): #省略其它代码 @property deftoc(self): returnself.rich_content.get("toc","") @property defbody_html(self): returnself.rich_content.get("content","") @cached_property defrich_content(self): returngenerate_rich_content(self.body)
首先看到rich_content这个方法,它返回的是generate_rich_content函数调用后的结果,即将body属性的值经Markdown解析后的内容。但要注意的是我们使用了django提供的cached_property装饰器,这个装饰器和Python内置的property装饰器功能一样,可以将方法转为属性,这样就能够以属性访问的方式获取方法返回的值,不过cached_property进一步提供缓存功能,它将被装饰方法调用返回的值缓存起来,下次访问时将直接读取缓存内容,而不需重复执行方法获取返回结果。例如对博客文章内容的Markdown解析是比较耗时的,而解析的结果可能被多次访问,因此将其缓存起来能起到优化作用。
为了更方便地获取文章的HTML格式的内容和目录,我们进一步将generate_rich_content返回的值放到toc和body_html两个属性中,这里两个属性都从rich_content中取值,cached_property的作用就发挥出来了。
添加URL
接下来就是指定URL模式,让人们访问这个URL后就可以看到Feed生成的内容。通常RSS的URL配置直接写在项目的urls.py文件里,即打开blogproject/urls.py文件,添加如下代码:
blogproject/urls.py
fromblog.feedsimportAllPostsRssFeed urlpatterns=[ path('admin/',admin.site.urls), path('',include('blog.urls')), path('',include('comments.urls')), #记得在顶部引入AllPostsRssFeed path('all/rss/',AllPostsRssFeed(),name='rss'), ]
修改模板
简单修改一下templates/base.html模板,把RSS的URL添加到模板中:
RSS订阅
此外,由于将Markdown解析的内容放在了body_html属性,将之前博客文章详情模板里获取文章内容的代码也做相应调整:
templates/blog/detail.html
{{post.body_html|safe}}