瀏覽代碼

Test case and docs for custom context data in feeds

Thanks Paul Winkler for the initial patch. (Ref #18112).
Zbigniew Siciarz 12 年之前
父節點
當前提交
0a8402eb05

+ 14 - 2
django/contrib/syndication/views.py

@@ -100,6 +100,16 @@ class Feed(object):
     def get_object(self, request, *args, **kwargs):
         return None
 
+    def get_context_data(self, **kwargs):
+        """
+        Returns a dictionary to use as extra context if either
+        ``self.description_template`` or ``self.item_template`` are used.
+
+        Default implementation preserves the old behavior
+        of using {'obj': item, 'site': current_site} as the context.
+        """
+        return {'obj': kwargs.get('item'), 'site': kwargs.get('site')}
+
     def get_feed(self, obj, request):
         """
         Returns a feedgenerator.DefaultFeed object, fully populated, for
@@ -146,12 +156,14 @@ class Feed(object):
                 pass
 
         for item in self.__get_dynamic_attr('items', obj):
+            context = self.get_context_data(item=item, site=current_site,
+                                            obj=obj, request=request)
             if title_tmp is not None:
-                title = title_tmp.render(RequestContext(request, {'obj': item, 'site': current_site}))
+                title = title_tmp.render(RequestContext(request, context))
             else:
                 title = self.__get_dynamic_attr('item_title', item)
             if description_tmp is not None:
-                description = description_tmp.render(RequestContext(request, {'obj': item, 'site': current_site}))
+                description = description_tmp.render(RequestContext(request, context))
             else:
                 description = self.__get_dynamic_attr('item_description', item)
             link = add_domain(

+ 54 - 0
docs/ref/contrib/syndication.txt

@@ -137,6 +137,51 @@ into those elements.
 
   See `a complex example`_ below that uses a description template.
 
+  There is also a way to pass additional information to title and description
+  templates, if you need to supply more than the two variables mentioned
+  before. You can provide your implementation of ``get_context_data`` method
+  in your Feed subclass. For example::
+
+    from mysite.models import Article
+    from django.contrib.syndication.views import Feed
+
+    class ArticlesFeed(Feed):
+        title = "My articles"
+        description_template = "feeds/articles.html"
+
+        def items(self):
+            return Article.objects.order_by('-pub_date')[:5]
+
+        def get_context_data(self, **kwargs):
+            context = super(ArticlesFeed, self).get_context_data(**kwargs)
+            context['foo'] = 'bar'
+            return context
+
+  And the template:
+
+  .. code-block:: html+django
+
+    Something about {{ foo }}: {{ obj.description }}
+
+  This method will be called once per each item in the list returned by
+  ``items()`` with the following keyword arguments:
+
+  * ``item``: the current item. For backward compatibility reasons, the name
+    of this context variable is ``{{ obj }}``.
+
+  * ``obj``: the object returned by ``get_object()``. By default this is not
+    exposed to the templates to avoid confusion with ``{{ obj }}`` (see above),
+    but you can use it in your implementation of ``get_context_data()``.
+
+  * ``site``: current site as described above.
+
+  * ``request``: current request.
+
+  The behavior of ``get_context_data()`` mimics that of
+  :ref:`generic views <adding-extra-context>` - you're supposed to call
+  ``super()`` to retrieve context data from parent class, add your data
+  and return the modified dictionary.
+
 * To specify the contents of ``<link>``, you have two options. For each item
   in ``items()``, Django first tries calling the
   ``item_link()`` method on the
@@ -599,6 +644,15 @@ This example illustrates all possible attributes and methods for a
 
         item_description = 'A description of the item.' # Hard-coded description.
 
+        def get_context_data(self, **kwargs):
+            """
+            Returns a dictionary to use as extra context if either
+            description_template or item_template are used.
+
+            Default implementation preserves the old behavior
+            of using {'obj': item, 'site': current_site} as the context.
+            """
+
         # ITEM LINK -- One of these three is required. The framework looks for
         # them in this order.
 

+ 2 - 0
docs/topics/class-based-views/generic-display.txt

@@ -188,6 +188,8 @@ Providing a useful ``context_object_name`` is always a good idea. Your
 coworkers who design templates will thank you.
 
 
+.. _adding-extra-context:
+
 Adding extra context
 --------------------
 

+ 13 - 0
tests/regressiontests/syndication/feeds.py

@@ -97,6 +97,19 @@ class TemplateFeed(TestRss2Feed):
         return "Not in a template"
 
 
+class TemplateContextFeed(TestRss2Feed):
+    """
+    A feed to test custom context data in templates for title or description.
+    """
+    title_template = 'syndication/title_context.html'
+    description_template = 'syndication/description_context.html'
+
+    def get_context_data(self, **kwargs):
+        context = super(TemplateContextFeed, self).get_context_data(**kwargs)
+        context['foo'] = 'bar'
+        return context
+
+
 class NaiveDatesFeed(TestAtomFeed):
     """
     A feed with naive (non-timezone-aware) dates.

+ 1 - 0
tests/regressiontests/syndication/templates/syndication/description_context.html

@@ -0,0 +1 @@
+{{ obj }} (foo is {{ foo }})

+ 1 - 0
tests/regressiontests/syndication/templates/syndication/title_context.html

@@ -0,0 +1 @@
+{{ obj }} (foo is {{ foo }})

+ 16 - 0
tests/regressiontests/syndication/tests.py

@@ -323,6 +323,22 @@ class SyndicationFeedTest(FeedTestCase):
             'link': 'http://example.com/blog/1/',
         })
 
+    def test_template_context_feed(self):
+        """
+        Test that custom context data can be passed to templates for title
+        and description.
+        """
+        response = self.client.get('/syndication/template_context/')
+        doc = minidom.parseString(response.content)
+        feed = doc.getElementsByTagName('rss')[0]
+        chan = feed.getElementsByTagName('channel')[0]
+        items = chan.getElementsByTagName('item')
+
+        self.assertChildNodeContent(items[0], {
+            'title': 'My first entry (foo is bar)',
+            'description': 'My first entry (foo is bar)',
+        })
+
     def test_add_domain(self):
         """
         Test add_domain() prefixes domains onto the correct URLs.

+ 1 - 0
tests/regressiontests/syndication/urls.py

@@ -21,4 +21,5 @@ urlpatterns = patterns('django.contrib.syndication.views',
     (r'^syndication/feedurl/$', feeds.TestFeedUrlFeed()),
     (r'^syndication/articles/$', feeds.ArticlesFeed()),
     (r'^syndication/template/$', feeds.TemplateFeed()),
+    (r'^syndication/template_context/$', feeds.TemplateContextFeed()),
 )