public
Description: The source code for the Django Plugables application index.
Homepage: http://djangoplugables.com/
Clone URL: git://github.com/revyver/django-plugables.git
Search Repo:
Moving django-template-utils and typogrify to pluggable's trunk.

git-svn-id: 
http://svn.revyver.com/djangopluggables.com/trunk/applications@198 
5c07df8f-9e68-4da1-b9d9-3219ff5d644b
revyver (author)
Sat Apr 05 19:48:41 -0700 2008
commit  8466807441a3724dc9542b0b71d5f004e6342faa
tree    cc07935642034a345ed246f249fe430c0ece45ec
parent  d3a556afce4942cbb7fbf196af4cb07e829400eb
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
0
@@ -1 +1,33 @@
0
+"""
0
+A generic function for generating context processors, and a processor
0
+which adds media-specific settings to each ``RequestContext``.
0
+
0
+"""
0
+
0
+def settings_processor(*settings_list):
0
+ """
0
+ Generates and returns a context processor function which will read
0
+ the values of all the settings passed in and return them in each
0
+ ``RequestContext`` in which it is applied.
0
+
0
+ For example::
0
+
0
+ my_settings_processor = settings_processor('INTERNAL_IPS', 'SITE_ID')
0
+
0
+ ``my_settings_processor`` would then be a valid context processor
0
+ which would return the values of the settings ``INTERNAL_IPS`` and
0
+ ``SITE_ID`` in each ``RequestContext`` in which it was applied.
0
+
0
+ """
0
+ def _processor(request):
0
+ from django.conf import settings
0
+ settings_dict = {}
0
+ for setting_name in settings_list:
0
+ settings_dict[setting_name] = getattr(settings, setting_name)
0
+ return settings_dict
0
+ return _processor
0
+
0
+media = settings_processor('ADMIN_MEDIA_PREFIX', 'MEDIA_URL')
0
+media.__doc__ = """A context processor which adds the values of the settings
0
+``ADMIN_MEDIA_PREFIX`` and ``MEDIA_URL`` to a ``RequestContext``."""
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
0
@@ -1 +1,216 @@
0
+"""
0
+Utilities for text-to-HTML conversion.
0
+
0
+"""
0
+
0
+
0
+def textile(text, **kwargs):
0
+ """
0
+ Applies Textile conversion to a string, and returns the HTML.
0
+
0
+ This is simply a pass-through to the ``textile`` template filter
0
+ included in ``django.contrib.markup``, which works around issues
0
+ PyTextile has with Unicode strings. If you're not using Django but
0
+ want to use Textile with ``MarkupFormatter``, you'll need to
0
+ supply your own Textile filter.
0
+
0
+ """
0
+ from django.contrib.markup.templatetags.markup import textile
0
+ return textile(text)
0
+
0
+def markdown(text, **kwargs):
0
+ """
0
+ Applies Markdown conversion to a string, and returns the HTML.
0
+
0
+ """
0
+ import markdown
0
+ return markdown.markdown(text, **kwargs)
0
+
0
+def restructuredtext(text, **kwargs):
0
+ """
0
+ Applies reStructuredText conversion to a string, and returns the
0
+ HTML.
0
+
0
+ """
0
+ from docutils import core
0
+ parts = core.publish_parts(source=text,
0
+ writer_name='html4css1',
0
+ **kwargs)
0
+ return parts['fragment']
0
+
0
+DEFAULT_MARKUP_FILTERS = {
0
+ 'textile': textile,
0
+ 'markdown': markdown,
0
+ 'restructuredtext': restructuredtext
0
+ }
0
+
0
+
0
+class MarkupFormatter(object):
0
+ """
0
+ Generic markup formatter which can handle multiple text-to-HTML
0
+ conversion systems.
0
+
0
+
0
+ Overview
0
+ ========
0
+
0
+ Conversion is handled by filter functions registered with an
0
+ instance; a set of default filters is provided which cover
0
+ Markdown, reStructuredText and Textile (though using one of these
0
+ requires the appropriate module to be available on your system --
0
+ e.g., using the reST filter requires you to have ``docutils``
0
+ installed).
0
+
0
+ New filters can be added by registering them with an instance;
0
+ simply define a function which performs the conversion you want,
0
+ and use the ``register`` method to add it; ``register`` expects
0
+ two arguments:
0
+
0
+ 1. The name to associate with the filter.
0
+
0
+ 2. The actual filter function.
0
+
0
+ So, for example, you might define a new filter function called
0
+ ``my_filter``, and register it like so::
0
+
0
+ formatter = MarkupFormatter()
0
+ formatter.register('my_filter', my_filter)
0
+
0
+ Instances are callable, so applying the conversion to a string is
0
+ simple::
0
+
0
+ my_html = formatter(my_string, filter_name='my_filter')
0
+
0
+ The filter to use for conversion is determined in either of two
0
+ ways:
0
+
0
+ 1. If the keyword argument ``filter_name`` is supplied, it will be
0
+ used as the filter name.
0
+
0
+ 2. Absent an explicit argument, the filter name will be taken from
0
+ the ``MARKUP_FILTER`` setting in your Django settings file (see
0
+ below).
0
+
0
+ Additionally, arbitrary keyword arguments can be supplied, and
0
+ they will be passed on to the filter function.
0
+
0
+
0
+ Reading default bahavior from a Django setting
0
+ ==============================================
0
+
0
+ The Django setting ``MARKUP_FILTER`` can be used to specify
0
+ default behavior; if used, its value should be a 2-tuple:
0
+
0
+ * The first element should be the name of a filter.
0
+
0
+ * The second element should be a dictionary to use as keyword
0
+ arguments for that filter.
0
+
0
+ So, for example, to have the default behavior apply Markdown with
0
+ safe mode enabled, you would add this to your Django settings
0
+ file::
0
+
0
+ MARKUP_FILTER = ('markdown', { 'safe_mode': True })
0
+
0
+ The filter named in this setting does not have to be from the
0
+ default set; as long as you register a filter of that name before
0
+ trying to use the formatter, it will work.
0
+
0
+ To have the default behavior apply no conversion whatsoever, set
0
+ ``MARKUP_FILTER`` like so::
0
+
0
+ MARKUP_FILTER = (None, {})
0
+
0
+ When the ``filter_name`` keyword argument is supplied, the
0
+ ``MARKUP_FILTER`` setting is ignored entirely -- neither a filter
0
+ name nor any keyword arguments will be read from it. This means
0
+ that, by always supplying ``filter_name`` explicitly, it is
0
+ possible to use this formatter without configuring or even
0
+ installing Django.
0
+
0
+
0
+ Django and template autoescaping
0
+ ================================
0
+
0
+ Django's template system defaults to escaping the output of
0
+ template variables, which can interfere with functions intended to
0
+ return HTML. ``MarkupFormatter`` does not in any way tamper with
0
+ Django's autoescaping, so pasing the results of formatting
0
+ directly to a Django template will result in that text being
0
+ escaped.
0
+
0
+ If you need to use ``MarkupFormatter`` for items which will be
0
+ passed to a Django template as variables, use the function
0
+ ``django.utils.safestring.mark_safe`` to tell Django's template
0
+ system not to escape that text.
0
+
0
+ For convenience, a Django template filter is included (in
0
+ ``templatetags/generic_markup.py``) which applies
0
+ ``MarkupFormatter`` to a string and marks the result as not
0
+ requiring autoescaping.
0
+
0
+
0
+ Examples
0
+ ========
0
+
0
+ Using the default behavior, with the filter name and arguments
0
+ taken from the ``MARKUP_FILTER`` setting::
0
+
0
+ formatter = MarkupFormatter()
0
+ my_string = 'Lorem ipsum dolor sit amet.\n\nConsectetuer adipiscing elit.'
0
+ my_html = formatter(my_string)
0
+
0
+ Explicitly naming the filter to use::
0
+
0
+ my_html = formatter(my_string, filter_name='markdown')
0
+
0
+ Passing keyword arguments::
0
+
0
+ my_html = formatter(my_string, filter_name='markdown', safe_mode=True)
0
+
0
+ Perform no conversion (return the text as-is)::
0
+
0
+ my_html = formatter(my_string, filter_name=None)
0
+
0
+ """
0
+ def __init__(self):
0
+ self._filters = {}
0
+ for filter_name, filter_func in DEFAULT_MARKUP_FILTERS.items():
0
+ self.register(filter_name, filter_func)
0
+
0
+ def register(self, filter_name, filter_func):
0
+ """
0
+ Registers a new filter for use.
0
+
0
+ """
0
+ self._filters[filter_name] = filter_func
0
+
0
+ def __call__(self, text, **kwargs):
0
+ """
0
+ Applies text-to-HTML conversion to a string, and returns the
0
+ HTML.
0
+
0
+ """
0
+ if 'filter_name' in kwargs:
0
+ filter_name = kwargs['filter_name']
0
+ del kwargs['filter_name']
0
+ filter_kwargs = {}
0
+ else:
0
+ from django.conf import settings
0
+ filter_name, filter_kwargs = settings.MARKUP_FILTER
0
+ if filter_name is None:
0
+ return text
0
+ if filter_name not in self._filters:
0
+ raise ValueError("'%s' is not a registered markup filter. Registered filters are: %s." % (filter_name,
0
+ ', '.join(self._filters.iterkeys())))
0
+ filter_func = self._filters[filter_name]
0
+ filter_kwargs.update(**kwargs)
0
+ return filter_func(text, **filter_kwargs)
0
+
0
+
0
+# Unless you need to have multiple instances of MarkupFormatter lying
0
+# around, or want to subclass it, the easiest way to use it is to
0
+# import this instance.
0
+
0
+formatter = MarkupFormatter()
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
0
@@ -1 +1,73 @@
0
+"""
0
+Subclass of ``template.Node`` for easy context updating.
0
+
0
+"""
0
+
0
+from django.db.models import get_model
0
+from django.conf import settings
0
+from django import template
0
+
0
+
0
+class ContextUpdatingNode(template.Node):
0
+ """
0
+ Node that updates the context with certain values.
0
+
0
+ Subclasses should define ``get_content()``, which should return a
0
+ dictionary to be added to the context.
0
+
0
+ """
0
+ def render(self, context):
0
+ context.update(self.get_content(context))
0
+ return ''
0
+
0
+ def get_content(self, context):
0
+ raise NotImplementedError
0
+
0
+
0
+class GenericContentNode(ContextUpdatingNode):
0
+ """
0
+ Base Node class for retrieving objects from any model.
0
+
0
+ By itself, this class will retrieve a number of objects from a
0
+ particular model (specified by an "app_name.model_name" string)
0
+ and store them in a specified context variable (these are the
0
+ ``num``, ``model`` and ``varname`` arguments to the constructor,
0
+ respectively), but is also intended to be subclassed for
0
+ customization.
0
+
0
+ There are two ways to add extra bits to the eventual database
0
+ lookup:
0
+
0
+ 1. Add the setting ``GENERIC_CONTENT_LOOKUP_KWARGS`` to your
0
+ settings file; this should be a dictionary whose keys are
0
+ "app_name.model_name" strings corresponding to models, and whose
0
+ values are dictionaries of keyword arguments which will be
0
+ passed to ``filter()``.
0
+
0
+ 2. Subclass and override ``_get_query_set``; all that's expected
0
+ is that it will return a ``QuerySet`` which will be used to
0
+ retrieve the object(s). The default ``QuerySet`` for the
0
+ specified model (filtered as described above) will be available
0
+ as ``self.query_set`` if you want to work with it.
0
+
0
+ """
0
+ def __init__(self, model, num, varname):
0
+ self.num = num
0
+ self.varname = varname
0
+ lookup_dict = getattr(settings, 'GENERIC_CONTENT_LOOKUP_KWARGS', {})
0
+ self.model = get_model(*model.split('.'))
0
+ if self.model is None:
0
+ raise template.TemplateSyntaxError("Generic content tag got invalid model: %s" % model)
0
+ self.query_set = self.model._default_manager.filter(**lookup_dict.get(model, {}))
0
+
0
+ def _get_query_set(self):
0
+ return self.query_set
0
+
0
+ def get_content(self, context):
0
+ query_set = self._get_query_set()
0
+ if self.num == 1:
0
+ result = query_set[0]
0
+ else:
0
+ result = list(query_set[:self.num])
0
+ return { self.varname: result }
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
0
@@ -1 +1,88 @@
0
+"""
0
+Tags for performing basic value comparisons in templates.
0
+
0
+"""
0
+
0
+
0
+from django import template
0
+
0
+
0
+COMPARISON_DICT = {
0
+ 'less': lambda x: x < 0,
0
+ 'less_or_equal': lambda x: x <= 0,
0
+ 'greater_or_equal': lambda x: x >= 0,
0
+ 'greater': lambda x: x > 0,
0
+ }
0
+
0
+
0
+class ComparisonNode(template.Node):
0
+ def __init__(self, var1, var2, comparison, nodelist_true, nodelist_false):
0
+ self.var1 = template.Variable(var1)
0
+ self.var2 = template.Variable(var2)
0
+ self.comparison = comparison
0
+ self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
0
+
0
+ def render(self, context):
0
+ try:
0
+ result = cmp(self.var1.resolve(context),
0
+ self.var2.resolve(context))
0
+ if COMPARISON_DICT[self.comparison](result):
0
+ return self.nodelist_true.render(context)
0
+ # If either variable fails to resolve, return nothing.
0
+ except template.VariableDoesNotExist:
0
+ return ''
0
+ # If the types don't permit comparison, return nothing.
0
+ except TypeError:
0
+ return ''
0
+ return self.nodelist_false.render(context)
0
+
0
+
0
+def do_comparison(parser, token):
0
+ """
0
+ Compares two values.
0
+
0
+ Syntax::
0
+
0
+ {% if_[comparison] [var1] [var2] %}
0
+ ...
0
+ {% else %}
0
+ ...
0
+ {% endif_[comparison] %}
0
+
0
+ The {% else %} block is optional, and ``var1`` and ``var2`` may be
0
+ variables or literal values.
0
+
0
+ Supported comparisons are ``less``, ``less_or_equal``, ``greater``
0
+ and ``greater_or_equal``.
0
+
0
+ Examples::
0
+
0
+ {% if_less some_object.id 3 %}
0
+ <p>{{ some_object }} has an id less than 3.</p>
0
+ {% endif_less %}
0
+
0
+ {% if_greater_or_equal forloop.counter 4 %}
0
+ <p>This is at least the fifth time through the loop.</p>
0
+ {% else %}
0
+ <p>This is one of the first four trips through the loop.</p>
0
+ {% endif_greater_or_equal %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 3:
0
+ raise template.TemplateSyntaxError("'%s' tag takes two arguments" % bits[0])
0
+ end_tag = 'end' + bits[0]
0
+ nodelist_true = parser.parse(('else', end_tag))
0
+ token = parser.next_token()
0
+ if token.contents == 'else':
0
+ nodelist_false = parser.parse((end_tag,))
0
+ parser.delete_first_token()
0
+ else:
0
+ nodelist_false = template.NodeList()
0
+ comparison = bits[0].split('if_')[1]
0
+ return ComparisonNode(bits[1], bits[2], comparison, nodelist_true, nodelist_false)
0
+
0
+register = template.Library()
0
+for tag_name in ('if_less', 'if_less_or_equal', 'if_greater_or_equal', 'if_greater'):
0
+ register.tag(tag_name, do_comparison)
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
0
@@ -1 +1,146 @@
0
+"""
0
+Tags which can retrieve and parse RSS and Atom feeds, and return the
0
+results for use in templates.
0
+
0
+Based, in part, on the original idea by user baumer1122 and posted to
0
+djangosnippets at http://www.djangosnippets.org/snippets/311/
0
+
0
+"""
0
+
0
+import datetime
0
+import feedparser
0
+from django import template
0
+from django.template.loader import render_to_string
0
+
0
+from template_utils.nodes import ContextUpdatingNode
0
+
0
+
0
+class FeedIncludeNode(template.Node):
0
+ def __init__(self, feed_url, template_name, num_items=None):
0
+ self.feed_url = template.Variable(feed_url)
0
+ self.num_items = num_items
0
+ self.template_name = template_name
0
+
0
+ def render(self, context):
0
+ feed_url = self.feed_url.resolve(context)
0
+ feed = feedparser.parse(feed_url)
0
+ items = []
0
+ num_items = int(self.num_items) or len(feed['entries'])
0
+ for i in range(num_items):
0
+ pub_date = feed['entries'][i].updated_parsed
0
+ published = datetime.date(pub_date[0], pub_date[1], pub_date[2])
0
+ items.append({ 'title': feed['entries'][i].title,
0
+ 'summary': feed['entries'][i].summary,
0
+ 'link': feed['entries'][i].link,
0
+ 'date': published })
0
+ return render_to_string(self.template_name, { 'items': items,
0
+ 'feed': feed })
0
+
0
+
0
+class FeedParserNode(ContextUpdatingNode):
0
+ def __init__(self, feed_url, varname):
0
+ self.feed_url = template.Variable(feed_url)
0
+ self.varname = varname
0
+
0
+ def get_content(self, context):
0
+ feed_url = self.feed_url.resolve(context)
0
+ return { self.varname: feedparser.parse(feed_url) }
0
+
0
+
0
+def do_include_feed(parser, token):
0
+ """
0
+ Parse an RSS or Atom feed and render a given number of its items
0
+ into HTML.
0
+
0
+ It is **highly** recommended that you use `Django's template
0
+ fragment caching`_ to cache the output of this tag for a
0
+ reasonable amount of time (e.g., one hour); polling a feed too
0
+ often is impolite, wastes bandwidth and may lead to the feed
0
+ provider banning your IP address.
0
+
0
+ .. _Django's template fragment caching: http://www.djangoproject.com/documentation/cache/#template-fragment-caching
0
+
0
+ Arguments should be:
0
+
0
+ 1. The URL of the feed to parse.
0
+
0
+ 2. The number of items to render (if not supplied, renders all
0
+ items in the feed).
0
+
0
+ 3. The name of a template to use for rendering the results into HTML.
0
+
0
+ The template used to render the results will receive two variables:
0
+
0
+ ``items``
0
+ A list of dictionaries representing feed items, each with
0
+ 'title', 'summary', 'link' and 'date' members.
0
+
0
+ ``feed``
0
+ The feed itself, for pulling out arbitrary attributes.
0
+
0
+ Requires the Universal Feed Parser, which can be obtained at
0
+ http://feedparser.org/. See `its documentation`_ for details of the
0
+ parsed feed object.
0
+
0
+ .. _its documentation: http://feedparser.org/docs/
0
+
0
+ Syntax::
0
+
0
+ {% include_feed [feed_url] [num_items] [template_name] %}
0
+
0
+ Example::
0
+
0
+ {% include_feed "http://www2.ljworld.com/rss/headlines/" 10 feed_includes/ljworld_headlines.html %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) == 3:
0
+ return FeedIncludeNode(feed_url=bits[1], template_name=bits[2])
0
+ elif len(bits) == 4:
0
+ return FeedIncludeNode(feed_url=bits[1], num_items=bits[2], template_name=bits[3])
0
+ else:
0
+ raise template.TemplateSyntaxError("'%s' tag takes either two or three arguments" % bits[0])
0
+
0
+def do_parse_feed(parser, token):
0
+ """
0
+ Parses a given feed and returns the result in a given context
0
+ variable.
0
+
0
+ It is **highly** recommended that you use `Django's template
0
+ fragment caching`_ to cache the output of this tag for a
0
+ reasonable amount of time (e.g., one hour); polling a feed too
0
+ often is impolite, wastes bandwidth and may lead to the feed
0
+ provider banning your IP address.
0
+
0
+ .. _Django's template fragment caching: http://www.djangoproject.com/documentation/cache/#template-fragment-caching
0
+
0
+ Arguments should be:
0
+
0
+ 1. The URL of the feed to parse.
0
+
0
+ 2. The name of a context variable in which to return the result.
0
+
0
+ Requires the Universal Feed Parser, which can be obtained at
0
+ http://feedparser.org/. See `its documentation`_ for details of the
0
+ parsed feed object.
0
+
0
+ .. _its documentation: http://feedparser.org/docs/
0
+
0
+ Syntax::
0
+
0
+ {% parse_feed [feed_url] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% parse_feed "http://www2.ljworld.com/rss/headlines/" as ljworld_feed %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 4:
0
+ raise template.TemplateSyntaxError(u"'%s' tag takes three arguments" % bits[0])
0
+ return FeedParserNode(bits[1], bits[3])
0
+
0
+register = template.Library()
0
+register.tag('include_feed', do_include_feed)
0
+register.tag('parse_feed', do_parse_feed)
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
0
@@ -1 +1,158 @@
0
+"""
0
+Template tags which can do retrieval of content from any model.
0
+
0
+"""
0
+
0
+
0
+from django import template
0
+from django.db.models import get_model
0
+
0
+from template_utils.nodes import ContextUpdatingNode, GenericContentNode
0
+
0
+
0
+class RandomObjectsNode(GenericContentNode):
0
+ """
0
+ A subclass of ``GenericContentNode`` which overrides
0
+ ``_get_query_set`` to apply random ordering.
0
+
0
+ """
0
+ def _get_query_set(self):
0
+ return self.query_set.order_by('?')
0
+
0
+
0
+class RetrieveObjectNode(ContextUpdatingNode):
0
+ """
0
+ ``Node`` subclass which retrieves a single object -- by
0
+ primary-key lookup -- from a given model.
0
+
0
+ Because this is a primary-key lookup, it is assumed that no other
0
+ filtering is needed; hence, the settings-based filtering performed
0
+ by ``GenericContentNode`` is not used here.
0
+
0
+ """
0
+ def __init__(self, model, pk, varname):
0
+ self.pk = template.Variable(pk)
0
+ self.varname = varname
0
+ self.model = get_model(*model.split('.'))
0
+ if self.model is None:
0
+ raise template.TemplateSyntaxError("Generic content tag got invalid model: %s" % model)
0
+
0
+ def get_content(self, context):
0
+ return { self.varname: self.model._default_manager.get(pk=self.pk.resolve(context))}
0
+
0
+
0
+def do_latest_object(parser, token):
0
+ """
0
+ Retrieves the latest object from a given model, in that model's
0
+ default ordering, and stores it in a context variable.
0
+
0
+ Syntax::
0
+
0
+ {% get_latest_object [app_name].[model_name] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% get_latest_object comments.freecomment as latest_comment %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 4:
0
+ raise template.TemplateSyntaxError("'%s' tag takes three arguments" % bits[0])
0
+ if bits [2] != 'as':
0
+ raise template.TemplateSyntaxError("second argument to '%s' tag must be 'as'" % bits[0])
0
+ return GenericContentNode(bits[1], 1, bits[3])
0
+
0
+
0
+def do_latest_objects(parser, token):
0
+ """
0
+ Retrieves the latest ``num`` objects from a given model, in that
0
+ model's default ordering, and stores them in a context variable.
0
+
0
+ Syntax::
0
+
0
+ {% get_latest_objects [app_name].[model_name] [num] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% get_latest_objects comments.freecomment 5 as latest_comments %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 5:
0
+ raise template.TemplateSyntaxError("'%s' tag takes four arguments" % bits[0])
0
+ if bits [3] != 'as':
0
+ raise template.TemplateSyntaxError("third argument to '%s' tag must be 'as'" % bits[0])
0
+ return GenericContentNode(bits[1], bits[2], bits[4])
0
+
0
+def do_random_object(parser, token):
0
+ """
0
+ Retrieves a random object from a given model, and stores it in a
0
+ context variable.
0
+
0
+ Syntax::
0
+
0
+ {% get_random_object [app_name].[model_name] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% get_random_object comments.freecomment as random_comment %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 4:
0
+ raise template.TemplateSyntaxError("'%s' tag takes three arguments" % bits[0])
0
+ if bits [2] != 'as':
0
+ raise template.TemplateSyntaxError("second argument to '%s' tag must be 'as'" % bits[0])
0
+ return RandomObjectsNode(bits[1], 1, bits[3])
0
+
0
+
0
+def do_random_objects(parser, token):
0
+ """
0
+ Retrieves ``num`` random objects from a given model, and stores
0
+ them in a context variable.
0
+
0
+ Syntax::
0
+
0
+ {% get_random_objects [app_name].[model_name] [num] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% get_random_objects comments.freecomment 5 as random_comments %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 5:
0
+ raise template.TemplateSyntaxError("'%s' tag takes four arguments" % bits[0])
0
+ if bits [3] != 'as':
0
+ raise template.TemplateSyntaxError("third argument to '%s' tag must be 'as'" % bits[0])
0
+ return RandomObjectsNode(bits[1], bits[2], bits[4])
0
+
0
+
0
+def do_retrieve_object(parser, token):
0
+ """
0
+ Retrieves a specific object from a given model by primary-key
0
+ lookup, and stores it in a context variable.
0
+
0
+ Syntax::
0
+
0
+ {% retrieve_object [app_name].[model_name] [pk] as [varname] %}
0
+
0
+ Example::
0
+
0
+ {% retrieve_object flatpages.flatpage 12 as my_flat_page %}
0
+
0
+ """
0
+ bits = token.contents.split()
0
+ if len(bits) != 5:
0
+ raise template.TemplateSyntaxError("'%s' tag takes four arguments" % bits[0])
0
+ if bits[3] != 'as':
0
+ raise template.TemplateSyntaxError("third argument to '%s' tag must be 'as'" % bits[0])
0
+ return RetrieveObjectNode(bits[1], bits[2], bits[4])
0
+
0
+register = template.Library()
0
+register.tag('get_latest_object', do_latest_object)
0
+register.tag('get_latest_objects', do_latest_objects)
0
+regi