• Aucun résultat trouvé

Entries by Category: A More Complex Feed Example

Dans le document Projects Django (Page 162-167)

Now, you’d like to also offer categorized feeds so that readers who are interested in one or two specific topics can subscribe to feeds that only list entries from the categories they like. But this is a bit trickier because it raises two problems:

• The list of items in the feed should, of course, know how to figure out which Category it’s looking at and ensure that it only returns entries from that category.

• Several of the metadata fields—the title of the feed, the link, and so on—will need to change dynamically based on the category.

Django’s Feedclass provides a way to deal with this, though. A Feedsubclass can define a method called get_object(), which will be passed an argument containing the bits of the URL that came after the slug you registered the feed with, as a list. So, for example, if you registered a feed with the slug categoriesand visited the URL /feeds/categories/django/, your feed’s get_object()would be passed an argument containing the single-item list ["django"]. From there you can look up the category.

Let’s start by adding two items to the importstatements at the top of your feeds.pyfile so that now it looks like this:

from django.core.exceptions import ObjectDoesNotExist from django.utils.feedgenerator import Atom1Feed from django.contrib.sites.models import Site from django.contrib.syndication.feeds import Feed from coltrane.models import Category, Entry

This gives you access to the Categorymodel, as well as to an exception class Django defines—ObjectDoesNotExist. You can use this if someone tries to visit a URL for a nonexistent category’s feed. (When you raise ObjectDoesNotExist, Django will return an HTTP 404 “File Not Found” response.)

Now you can begin writing your feed class. Since a lot of it is similar to the existing LatestEntriesFeed, you’ll just subclass it and change the parts that need to be changed:

class CategoryFeed(LatestEntriesFeed):

def get_object(self, bits):

if len(bits) != 1:

raise ObjectDoesNotExist

return Category.objects.get(slug__exact=bits[0])

This will either raise ObjectDoesNotExistor return the Categoryyou need to display entries for. Now you can set up the feed’s title, description, and link, by defining methods with those names that receive the Categoryobject as an argument (Django’s feed system is smart enough to recognize that it needs to pass that object when calling them):

def title(self, obj):

return "%s: Latest entries in category '%s'" % (current_site.name, obj.title)

def description(self, obj):

return "%s: Latest entries in category '%s'" % (current_site.name, obj.title)

def link(self, obj):

return obj.get_absolute_url()

"PLAIN" ATTRIBUTES VS. METHODS ON FEEDS

In general, any of the various bits of metadata for a feed—the title, description and link, and metadata for individual items in the feed—can either be hard-coded by a plain attribute of the correct name or generated dynamically by defining a method of that name. For feeds like CategoryFeedthat need to look up some object (in this case a Category) through their get_object()method, you can define a method that expects to receive that object.

Again, for a full list of the different fields you can use on a feed—each of which will work like this—

consult the full documentation for django.contrib.syndicationonline at www.djangoproject.com/documentation/syndication_feeds/.

You can change the items()method as well. Again, Django’s feed system is smart enough to know that it needs to be passed the Categoryobject, and it will make sure that happens:

def items(self, obj):

return obj.live_entry_set()[:15]

Remember that you defined the live_entry_set()method on the Categorymodel so that it would only return entries with “live” status.

And that’s that. Now your feeds.pyfile should look like this:

from django.core.exceptions import ObjectDoesNotExist from django.utils.feedgenerator import Atom1Feed from django.contrib.sites.models import Site from django.contrib.syndication.feeds import Feed from coltrane.models import Category, Entry current_site = Site.objects.get_current() 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()]

class CategoryFeed(LatestEntriesFeed):

def get_object(self, bits):

if len(bits) != 1:

raise ObjectDoesNotExist

return Category.objects.get(slug__exact=bits[0]) def title(self, obj):

return "%s: Latest entries in category '%s'" % (current_site.name, obj.title)

def description(self, obj):

return "%s: Latest entries in category '%s'" % (current_site.name, obj.title)

def link(self, obj):

return obj.get_absolute_url() def items(self, obj):

return obj.live_entry_set()[:15]

You can register this feed by changing the importline in your project’s urls.pyfile from from coltrane.feeds import LatestEntriesFeed

to

from coltrane.feeds import CategoryFeed, LatestEntriesFeed and by adding one line to the feedsdictionary. Change it from feeds = { 'entries': LatestEntriesFeed }

to

feeds = { 'entries': LatestEntriesFeed, 'categories': CategoryFeed }

Finally, you’ll want to set up the templates feeds/categories_title.htmland feeds/

categories_title.html. Since they’re just displaying entries, feel free to copy and paste the contents of the two templates you used for the LatestEntriesFeed.

Writing feed classes that display entries or links by tag will follow the same pattern.

Examples are included in the sample code you can download for this book, but again, I’d recommend giving it a try yourself before you peek to see how it’s done.

Looking Ahead

And with that, you’ve implemented all the features you set out to have for your weblog. But, more important, you’ve covered a huge amount of territory within Django: models, views, URL routing, templating and custom template extensions, comments, and Django’s dis-patcher and syndication feeds. You should already be feeling a lot more comfortable working with Django and writing what would—if you were developing from scratch without Django’s help—be some fairly complex features.

So give yourself a pat on the back because you’ve got a lot of useful Django knowledge under your belt now. Also take some time to work with the weblog application you’ve devel-oped. Try to think of a feature or two you’d like to add, and then see if you can work out how to add them.

When you’re ready, the next chapter will start a brand-new application: a code-sharing site with some useful social features, which will highlight Django’s form-processing system for user-submitted content and show off some more advanced uses of the database API.

Dans le document Projects Django (Page 162-167)