Djangoで、配信フィードを生成する

2018-11-05 / PythonDjango

フィードとは

お気に入りのWebサイトの更新を知りたい、というのはよくあるケースです。今でこそプッシュ通知ができるようになりましたが、昔はそんなものありません。フィードはそんな時代に生まれた、Webサイトの更新を知らせるための専用のファイルです。フィードリーダーといった専用のソフトに登録することで、利用者は更新を受け取れるようになるのです。今でも愛用者は大勢いますし、今後も続いていくでしょう。Djangoはフィードを自動生成することができますので、今回紹介していきます。

サイトマップに似ていますが、それよりも全体的に単純です。

(準備)アプリケーションの作成

Djangoアプリケーションを作らないと試せないので、作成しておきます。今回はブログアプリケーションのようなものを例に説明していきます。

models.pyを次のようにしておきます。シンプルな記事モデルです。

class Post(models.Model):
    title = models.CharField('タイトル', max_length=255)
    text = models.TextField('本文')
    created_at = models.DateTimeField('作成日', default=timezone.now)

    def __str__(self):
        return self.title

アプリケーションのurls.pyを、次のようにしておきます。

from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('list/', views.PostIndex.as_view(), name='list'),
    path('detail/<int:pk>/', views.PostDetail.as_view(), name='detail'),
]

記事の一覧と記事の詳細ページっぽいものを定義しました。ビューの中身は適当に作っておいてください。ほんとうに、適当で大丈夫です。reverseresolve_urlでの逆引きができればよいので

フィードを作成する

プロジェクトのurls.pyを編集します。

from django.contrib import admin
from django.contrib.syndication.views import Feed
from django.shortcuts import resolve_url
from django.urls import path, include, reverse_lazy
from blog.models import Post


class LatestPostFeed(Feed):
    title = "naritoブログ 最新記事"
    link = reverse_lazy('blog:list')
    description = "naritoブログから、記事の最新情報をお届けします。"

    def items(self):
        return Post.objects.all()[:15]

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.text[:100]  # 本文の100文字目までをとりあえず説明に。

    def item_link(self, item):
        return resolve_url('blog:detail', pk=item.pk)


urlpatterns = [
    path('admin/', admin.site.urls),
    path('latest/feed/', LatestPostFeed()),
    path('', include('blog.urls')),
]

流れとしてはまず、urlpatternsリストにfeed用の定義をします。

path('latest/feed/', LatestPostFeed()),

そしてFeedクラスを作ります。

class LatestPostFeed(Feed):
    title = "naritoブログ 最新記事"
    link = reverse_lazy('blog:list')
    description = "naritoブログから、記事の最新情報をお届けします。"

    def items(self):
        return Post.objects.all()[:15]

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.text[:100]  # 本文の100文字目までをとりあえず説明に。

    def item_link(self, item):
        return resolve_url('blog:detail', pk=item.pk)

デフォルトではRSS 2.0が利用されますが、クラス属性でそれを変更できます。次の例は、Atomにした例です。

from django.utils.feedgenerator import Atom1Feed
...
...
class AtomLatestPostFeed(Feed):
    feed_type = Atom1Feed
    ...
    ...

実際に生成されるFeed(xml)を見せておきましょう。 以下のようなFeedが作成されます。

<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>naritoブログ 最新記事</title>
        <link>http://127.0.0.1:8000/</link>
        <description>naritoブログから、記事の最新情報をお届けします。</description>
        <atom:link href="http://127.0.0.1:8000/latest/feed/" rel="self"></atom:link>
        <language>ja</language>
        <lastBuildDate>Tue, 18 Sep 2018 15:14:51 +0000</lastBuildDate>
        <item>
            <title>新しい書籍を出します。</title>
            <link>http://127.0.0.1:8000/detail/3/</link>
            <description>11月が12月頃に.....</description>
            <guid>http://127.0.0.1:8000/detail/3/</guid>
        </item>
        <item>
            <title>回転ずしに行きました。</title>
            <link>http://127.0.0.1:8000/detail/2/</link>
            <description>花丸という寿司屋です。おいし...</description>
            <guid>http://127.0.0.1:8000/detail/2/</guid>
        </item>
        <item>
            <title>ココイチにいきました。</title>
            <link>http://127.0.0.1:8000/detail/1/</link>
            <description>安定しておいしいです....</description>
            <guid>http://127.0.0.1:8000/detail/1/</guid>
        </item>
    </channel>
</rss>

<channels>内のtitle, link, descriptionはそのままクラス属性の内容です。 <item>title, link, descriptionは、今回メソッドで定義している部分ですね。 サイトマップと同様に、各属性はメソッドでもクラス属性でも定義できたりします。

RSSとAtom両方配信する

RSSとAtomの両方を配信するということも簡単です。

class LatestPostFeed(Feed):
...
...
...
class AtomLatestPostFeed(LatestPostFeed):
    feed_type = Atom1Feed
    subtitle = LatestPostFeed.description


urlpatterns = [
    path('admin/', admin.site.urls),
    path('latest/feed/', LatestPostFeed()),
    path('atom/latest/feed/', AtomLatestPostFeed()),  # Atom
    path('', include('blog.urls')),
]

LatestPostFeedを継承しつつ、feed_typeをAtomにしました。Atomはdescriptionのかわりにsubtitleという属性になるので、それも設定します。

class AtomLatestPostFeed(LatestPostFeed):
    feed_type = Atom1Feed
    subtitle = LatestPostFeed.description

各要素がどんな意味を持つかは、分かりやすいページがあるので、そちらを確認してください。 どんな属性・メソッドを定義できるかは、Djangoのドキュメントがわかりやすいです。

フィードをアプリケーション内に定義する

アプリケーション内にfeeds.pyを作り、中身を以下のようにします。

from django.contrib.syndication.views import Feed
from django.shortcuts import resolve_url
from django.urls import reverse_lazy
from .models import Post


class LatestPostFeed(Feed):
    title = "naritoブログ 最新記事"
    link = reverse_lazy('blog:list')
    description = "naritoブログから、記事の最新情報をお届けします。"

    def items(self):
        return Post.objects.all()[:15]

    def item_title(self, item):
        return item.title

    def item_description(self, item):
        return item.text[:100]  # 本文の100文字目までをとりあえず説明に。

    def item_link(self, item):
        return resolve_url('blog:detail', pk=item.pk)

この配信フィードを、urls.pyで読み込みます。

from django.urls import path
from .feeds import LatestPostFeed
from . import views

app_name = 'blog'

urlpatterns = [
    path('list/', views.PostIndex.as_view(), name='list'),
    path('detail/<int:pk>/', views.PostDetail.as_view(), name='detail'),
    path('latest/feed/', LatestPostFeed(), name='feed'),
]

上の例だと、includeでアプリケーションのurls.pyを読み込むとフィード定義も必ず読み込まれてしまいます。それが嫌な場合はクラス定義だけしておき、それを配信するかどうかは利用者に任せると良いでしょう。配信したければ、ルートのurls.pyで次のように読み込んでもらえばOKです。

from blog.feeds import LatestPostFeed
...
...
urlpatterns = [
    path('admin/', admin.site.urls),
    path('latest/feed/', LatestPostFeed(), name='blog_feed'), 
    path('', include('blog.urls')),
]

フィードURLをページに表示する

後はフィードURLをWebサイト上のどこかでお知らせするだけです。それだけで、フィードに慣れた人間ならば「お、フィードあるのか。フィードリーダーに登録しておこう」という感じになります。

単純にやるならば、次のようにa要素を配置するだけです。

<a href="{% url 'blog:feed' %}">RSS</a>

Font Awesomeを使う例もよく見かけます。

<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
<a href="{% url 'blog:feed' %}"><i class="fas fa-rss"></i></a>

この記事の関連記事

Djangoで、サイトマップを自動生成する

2018-11-05 / PythonDjango

- Djangoにはサイトマップを自動生成する機能が標準で組み込まれていて、簡単に使うことができます。

Djangoで、Webサイトの更新情報を通知するシリーズ

2019-07-27 / Djangoシリーズ・まとめ

- Webサイトでは、更新をお知らせする機能がよくあります。例えばメールで最新情報を知らせたり、最近ではLineやブラウザのプッシュ通知で更新情報をお知らせする例もよく使われています。

コメント欄

記事にコメントする

まだコメントはありません。