• Aucun résultat trouvé

Adding Feeds

Dans le document Projects Django (Page 158-162)

The last features you want for your weblog is the ability to have RSS or Atom feeds of your entries and links. You also want to have custom feeds that handle, for example, entries in a specific category. Doing this from scratch—by writing view functions that retrieve a list of entries and render a template that creates the appropriate XML instead of an HTML page—

wouldn’t be too terribly hard. But because this is a common need for web sites, Django again provides some help to automate the process via the bundled application django.contrib.

syndication. At its core, django.contrib.syndicationprovides two things:

• A set of classes that represent feeds and that can be subclassed for easy customization

• A view that knows how to work with these classes to generate and serve the appropriate XML

To see how it works, let’s start by setting up an Atom feed for the latest entries posted to the weblog.

LatestEntriesFeed

Go into the coltranedirectory and create a new empty file, called feeds.py. At the top, add the following lines:

from django.utils.feedgenerator import Atom1Feed from django.contrib.sites.models import Site from django.contrib.syndication.feeds import Feed from coltrane.models import Entry

current_site = Site.objects.get_current()

Now you can start writing a feed class for the latest entries. Call it LatestEntriesFeed. It will be a subclass of the django.contrib.syndication.feeds.Feedclass you’re importing here.

First you need to fill in some required metadata. This is going to be an Atom feed, so sev-eral elements are required. (RSS feeds require less metadata, but it’s a good idea to include this information anyway, since additional metadata is more useful for people who use feeds.) Here’s an example:

class LatestEntriesFeed(Feed):

author_name = "Bob Smith"

copyright = "http://%s/about/copyright/" % current_site.domain description = "Latest entries posted to %s" % current_site.name feed_type = Atom1Feed

item_copyright = "http://%s/about/copyright/" % current_site.domain item_author_name = "Bob Smith"

item_author_link = "http://%s/" % current_site.domain link = "/feeds/entries/"

title = "%s: Latest entries" % current_site.name

Go ahead and fill in appropriate information for your own name and relevant metadata.

Note that while most of the items here will automatically vary according to the current site, I’ve hard-coded values into the two fields for authors and the linkfield.

For reusability across a wide variety of sites, this feed class can be subclassed to override only those values; or if you have a function that can determine the correct value for a given site, you can fill that in. (For example, you might use a reverse URL lookup to get the link field.) For a complete list of these fields and what you’re allowed to put in each one, check the full documentation for django.contrib.syndication, which is online at www.djangoproject.

com/documentation/syndication_feeds/.

Now you need to tell the feed how to find the items it’s supposed to contain. This will be the latest 15 live entries. You do this by adding a method named items()to the feed class, which will return those entries:

def items(self):

return Entry.live.all()[:15]

Each item needs to have a date listed in the feed. That’s accomplished by a method called item_pubdate(), which will receive an object as an argument and should return a dateor datetimeobject to use for that object. (The Feedclass will automatically format this appro-priately for the type of feed being used.) In the case of an Entry, that’s just the value of the pub_datefield:

def item_pubdate(self, item):

return item.pub_date

Each item also needs to have a unique identifier, called a GUID (short for globally unique identifier). This can be the idfield from the database, but it’s generally better to use something less transient. If you ever migrated to a new server or a different database, the idvalues might change during the transition, and the GUID for a particular entry would change when that happened.

For a situation like this, the ideal solution is something called a tag URI (uniform resource identifier). Tag URIs are a standard way of generating a unique identifier for some Internet resource, in a way that won’t change so long as that Internet resource continues to exist at the same address. If you’re interested in the full details of the standard, tag URIs are specified by IETF RFC 4151 (www.faqs.org/rfcs/rfc4151.html), but the basic idea is that a tag URI for an item consists of three parts:

1. The tag:string.

2. The domain for the item, followed by a comma, followed by a relevant date for the item, followed by a colon.

3. An identifying string that is unique for that domain and date.

For the date, you’ll use the pub_datefield of each entry. For the unique identifying string, you’ll use the result of its get_absolute_url()method, since that’s required to be unique.

The result, for example, is that the entry at www.example.com/2008/jan/12/example-entry/

would end up with a GUID of

tag:example.com,2008-01-12:/2008/jan/12/example-entry/

This meets all the requirements for a feed GUID. To implement this, you simply define a method on your feed class called item_guid(). Again, it receives an object as its argument:

def item_guid(self, item):

return "tag:%s,%s:%s" % (current_site.domain,

item.pub_date.strftime('%Y-%m-%d'), item.get_absolute_url())

One final thing you can add to your feed is a list of categories for each item. This will help feed aggregators to categorize the items you publish. You can do this by defining a method called item_categories:

def item_categories(self, item):

return [c.title for c in item.categories.all()]

A full example feed class, then, looks like this:

class LatestEntriesFeed(Feed):

author_name = "Bob Smith"

copyright = "http://%s/about/copyright/" % current_site.domain description = "Latest entries posted to %s" % current_site.name feed_type = Atom1Feed

item_copyright = "http://%s/about/copyright/" % current_site.domain item_author_name = "Bob Smith"

item_author_link = "http://%s/" % current_site.domain link = "/feeds/entries/"

title = "%s: Latest entries" % current_site.name def items(self):

return Entry.live.all()[:15]

def item_pubdate(self, item):

return item.pub_date def item_guid(self, item):

return "tag:%s,%s:%s" % (current_site.domain,

item.pub_date.strftime('%Y-%m-%d'), item.get_absolute_url())

def item_categories(self, item):

return [c.title for c in item.categories.all()]

Now you can set up a URL for this feed. Go to the urls.pyfile in the cmsproject directory, and add two things. First, near the top of the file (above the list of URL patterns), add the fol-lowing importstatement and dictionary definition:

from coltrane.feeds import LatestEntriesFeed feeds = { 'entries': LatestEntriesFeed }

Next, add a new pattern to the list of URLs:

(r'^feeds/(?P<url>.*)/$',

'django.contrib.syndication.views.feed', { 'feed_dict': feeds }),

This will route any URL beginning with /feeds/to the view in django.contrib.syndication, which handles feeds. The dictionary you set up maps between feed slugs, like entries, and specific feed classes.

One final thing you need to do is create two templates. django.contrib.syndicationuses the Django template system to render the title and main body of each item in the feed so that you can decide how you want to present each type of item. So go to the directory where you’ve been keeping templates for this project, and inside it create a new directory called feeds.

Inside that create two new files, called entries_title.htmland entries_description.html.

(The names to use come from the combination of the feed’s slug—in this case, entries—and whether the template is for the item’s title or its description.) Each of these templates will have access to two variables:

• obj: This is a specific item being included in the feed.

• site: This is the current Siteobject, as returned by Site.objects.get_current().

So for item titles, you can simply use each entry’s title. In the entries_title.html tem-plate, place the following:

{{ obj.title }}

For the description, you’ll use the same trick as the entry archive templates you set up in the last chapter. Display the excerpt_htmlfield if it has any content; otherwise, display the first 50 words of body_html. So in entries_description.html, fill in the following:

{% if obj.excerpt_html %}

{{ obj.excerpt_html|safe }}

{% else %}

{{ obj.body_html|truncatewords_html:"50"|safe }}

{% endif %}

Remember that Django’s template system automatically escapes HTML in variables, so you still have to use the safefilter. With the templates in place, you can launch the develop-ment server and visit the URL /feeds/entries/to see the feed of latest entries in the weblog.

Writing a feed for the latest links should be easy at this point. Try writing the

LatestLinksFeedclass yourself and set it up correctly. (Remember that links don’t have cate-gories associated with them, so you should either leave out the item_catecate-gories()method or rewrite it to return a list of tags.) A full example is in the sample code you can download for this book, so refer to it if you get lost.

Dans le document Projects Django (Page 158-162)