Django

Code

root/django/trunk/django/contrib/syndication/feeds.py

Revision 8414, 7.2 kB (checked in by jbronn, 3 months ago)

Fixed #6547, added support for GeoRSS feeds in django.contrib.gis.feeds; added the feed_extra_kwargs and item_extra_kwargs to the Feed baseclass so that it's possible for subclasses to add dynamic attributes.

  • Property svn:eol-style set to native
Line 
1 from datetime import datetime, timedelta
2
3 from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
4 from django.template import loader, Template, TemplateDoesNotExist
5 from django.contrib.sites.models import Site, RequestSite
6 from django.utils import feedgenerator
7 from django.utils.tzinfo import FixedOffset
8 from django.utils.encoding import smart_unicode, iri_to_uri
9 from django.conf import settings         
10 from django.template import RequestContext
11
12 def add_domain(domain, url):
13     if not (url.startswith('http://') or url.startswith('https://')):
14         # 'url' must already be ASCII and URL-quoted, so no need for encoding
15         # conversions here.
16         url = iri_to_uri(u'http://%s%s' % (domain, url))
17     return url
18
19 class FeedDoesNotExist(ObjectDoesNotExist):
20     pass
21
22 class Feed(object):
23     item_pubdate = None
24     item_enclosure_url = None
25     feed_type = feedgenerator.DefaultFeed
26     title_template = None
27     description_template = None
28
29     def __init__(self, slug, request):
30         self.slug = slug
31         self.request = request
32         self.feed_url = request.path
33         self.title_template_name = self.title_template or ('feeds/%s_title.html' % slug)
34         self.description_template_name = self.description_template or ('feeds/%s_description.html' % slug)
35
36     def item_link(self, item):
37         try:
38             return item.get_absolute_url()
39         except AttributeError:
40             raise ImproperlyConfigured, "Give your %s class a get_absolute_url() method, or define an item_link() method in your Feed class." % item.__class__.__name__
41
42     def __get_dynamic_attr(self, attname, obj, default=None):
43         try:
44             attr = getattr(self, attname)
45         except AttributeError:
46             return default
47         if callable(attr):
48             # Check func_code.co_argcount rather than try/excepting the
49             # function and catching the TypeError, because something inside
50             # the function may raise the TypeError. This technique is more
51             # accurate.
52             if hasattr(attr, 'func_code'):
53                 argcount = attr.func_code.co_argcount
54             else:
55                 argcount = attr.__call__.func_code.co_argcount
56             if argcount == 2: # one argument is 'self'
57                 return attr(obj)
58             else:
59                 return attr()
60         return attr
61
62     def feed_extra_kwargs(self, obj):
63         """
64         Returns an extra keyword arguments dictionary that is used when
65         initializing the feed generator.
66         """
67         return {}
68
69     def item_extra_kwargs(self, item):
70         """
71         Returns an extra keyword arguments dictionary that is used with
72         the `add_item` call of the feed generator.
73         """
74         return {}
75
76     def get_object(self, bits):
77         return None
78
79     def get_feed(self, url=None):
80         """
81         Returns a feedgenerator.DefaultFeed object, fully populated, for
82         this feed. Raises FeedDoesNotExist for invalid parameters.
83         """
84         if url:
85             bits = url.split('/')
86         else:
87             bits = []
88
89         try:
90             obj = self.get_object(bits)
91         except ObjectDoesNotExist:
92             raise FeedDoesNotExist
93
94         if Site._meta.installed:
95             current_site = Site.objects.get_current()
96         else:
97             current_site = RequestSite(self.request)
98        
99         link = self.__get_dynamic_attr('link', obj)
100         link = add_domain(current_site.domain, link)
101
102         feed = self.feed_type(
103             title = self.__get_dynamic_attr('title', obj),
104             subtitle = self.__get_dynamic_attr('subtitle', obj),
105             link = link,
106             description = self.__get_dynamic_attr('description', obj),
107             language = settings.LANGUAGE_CODE.decode(),
108             feed_url = add_domain(current_site.domain,
109                                   self.__get_dynamic_attr('feed_url', obj)),
110             author_name = self.__get_dynamic_attr('author_name', obj),
111             author_link = self.__get_dynamic_attr('author_link', obj),
112             author_email = self.__get_dynamic_attr('author_email', obj),
113             categories = self.__get_dynamic_attr('categories', obj),
114             feed_copyright = self.__get_dynamic_attr('feed_copyright', obj),
115             feed_guid = self.__get_dynamic_attr('feed_guid', obj),
116             ttl = self.__get_dynamic_attr('ttl', obj),
117             **self.feed_extra_kwargs(obj)
118         )
119
120         try:
121             title_tmp = loader.get_template(self.title_template_name)
122         except TemplateDoesNotExist:
123             title_tmp = Template('{{ obj }}')
124         try:
125             description_tmp = loader.get_template(self.description_template_name)
126         except TemplateDoesNotExist:
127             description_tmp = Template('{{ obj }}')
128
129         for item in self.__get_dynamic_attr('items', obj):
130             link = add_domain(current_site.domain, self.__get_dynamic_attr('item_link', item))
131             enc = None
132             enc_url = self.__get_dynamic_attr('item_enclosure_url', item)
133             if enc_url:
134                 enc = feedgenerator.Enclosure(
135                     url = smart_unicode(enc_url),
136                     length = smart_unicode(self.__get_dynamic_attr('item_enclosure_length', item)),
137                     mime_type = smart_unicode(self.__get_dynamic_attr('item_enclosure_mime_type', item))
138                 )
139             author_name = self.__get_dynamic_attr('item_author_name', item)
140             if author_name is not None:
141                 author_email = self.__get_dynamic_attr('item_author_email', item)
142                 author_link = self.__get_dynamic_attr('item_author_link', item)
143             else:
144                 author_email = author_link = None
145
146             pubdate = self.__get_dynamic_attr('item_pubdate', item)
147             if pubdate:
148                 now = datetime.now()
149                 utcnow = datetime.utcnow()
150
151                 # Must always subtract smaller time from larger time here.
152                 if utcnow > now:
153                     sign = -1
154                     tzDifference = (utcnow - now)
155                 else:
156                     sign = 1
157                     tzDifference = (now - utcnow)
158
159                 # Round the timezone offset to the nearest half hour.
160                 tzOffsetMinutes = sign * ((tzDifference.seconds / 60 + 15) / 30) * 30
161                 tzOffset = timedelta(minutes=tzOffsetMinutes)
162                 pubdate = pubdate.replace(tzinfo=FixedOffset(tzOffset))
163
164             feed.add_item(
165                 title = title_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
166                 link = link,
167                 description = description_tmp.render(RequestContext(self.request, {'obj': item, 'site': current_site})),
168                 unique_id = self.__get_dynamic_attr('item_guid', item, link),
169                 enclosure = enc,
170                 pubdate = pubdate,
171                 author_name = author_name,
172                 author_email = author_email,
173                 author_link = author_link,
174                 categories = self.__get_dynamic_attr('item_categories', item),
175                 item_copyright = self.__get_dynamic_attr('item_copyright', item),
176                 **self.item_extra_kwargs(item)
177             )
178         return feed
Note: See TracBrowser for help on using the browser.