1 """Default variable filters."""
4 import random
as random_module
6 from functools
import wraps
8 from django
.utils
.functional
import wraps
# Python 2.3, 2.4 fallback.
10 from django
.template
import Variable
, Library
11 from django
.conf
import settings
12 from django
.utils
.translation
import ugettext
, ungettext
13 from django
.utils
.encoding
import force_unicode
, iri_to_uri
14 from django
.utils
.safestring
import mark_safe
, SafeData
18 #######################
20 #######################
22 def stringfilter(func
):
24 Decorator for filters which should only receive unicode objects. The object
25 passed as the first positional argument will be converted to a unicode
28 def _dec(*args
, **kwargs
):
31 args
[0] = force_unicode(args
[0])
32 if isinstance(args
[0], SafeData
) and getattr(func
, 'is_safe', False):
33 return mark_safe(func(*args
, **kwargs
))
34 return func(*args
, **kwargs
)
36 # Include a reference to the real function (used to check original
37 # arguments by the template parser).
38 _dec
._decorated
_function
= getattr(func
, '_decorated_function', func
)
39 for attr
in ('is_safe', 'needs_autoescape'):
40 if hasattr(func
, attr
):
41 setattr(_dec
, attr
, getattr(func
, attr
))
42 return wraps(func
)(_dec
)
49 def addslashes(value
):
51 Adds slashes before quotes. Useful for escaping strings in CSV, for
52 example. Less useful for escaping JavaScript; use the ``escapejs``
55 return value
.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
56 addslashes
.is_safe
= True
57 addslashes
= stringfilter(addslashes
)
60 """Capitalizes the first character of the value."""
61 return value
and value
[0].upper() + value
[1:]
63 capfirst
= stringfilter(capfirst
)
77 # Escape every ASCII character with a value less than 32.
78 _js_escapes
= (_base_js_escapes
+
79 tuple([('%c' % z
, '\\x%02X' % z
) for z
in range(32)]))
82 """Hex encodes characters for use in JavaScript strings."""
83 for bad
, good
in _js_escapes
:
84 value
= value
.replace(bad
, good
)
86 escapejs
= stringfilter(escapejs
)
88 def fix_ampersands(value
):
89 """Replaces ampersands with ``&`` entities."""
90 from django
.utils
.html
import fix_ampersands
91 return fix_ampersands(value
)
92 fix_ampersands
.is_safe
=True
93 fix_ampersands
= stringfilter(fix_ampersands
)
95 def floatformat(text
, arg
=-1):
97 Displays a float to a specified number of decimal places.
99 If called without an argument, it displays the floating point number with
100 one decimal place -- but only if there's a decimal place to be displayed:
105 * {{ num1|floatformat }} displays "34.2"
106 * {{ num2|floatformat }} displays "34"
107 * {{ num3|floatformat }} displays "34.3"
109 If arg is positive, it will always display exactly arg number of decimal
112 * {{ num1|floatformat:3 }} displays "34.232"
113 * {{ num2|floatformat:3 }} displays "34.000"
114 * {{ num3|floatformat:3 }} displays "34.260"
116 If arg is negative, it will display arg number of decimal places -- but
117 only if there are places to be displayed:
119 * {{ num1|floatformat:"-3" }} displays "34.232"
120 * {{ num2|floatformat:"-3" }} displays "34"
121 * {{ num3|floatformat:"-3" }} displays "34.260"
125 except (ValueError, TypeError):
130 return force_unicode(f
)
133 except OverflowError:
134 return force_unicode(f
)
136 return mark_safe(u
'%d' % int(f
))
138 formatstr
= u
'%%.%df' % abs(d
)
139 return mark_safe(formatstr
% f
)
140 floatformat
.is_safe
= True
142 def iriencode(value
):
143 """Escapes an IRI value for use in a URL."""
144 return force_unicode(iri_to_uri(value
))
145 iriencode
.is_safe
= True
146 iriencode
= stringfilter(iriencode
)
148 def linenumbers(value
, autoescape
=None):
149 """Displays text with line numbers."""
150 from django
.utils
.html
import escape
151 lines
= value
.split(u
'\n')
152 # Find the maximum width of the line count, for use with zero padding
153 # string format command
154 width
= unicode(len(unicode(len(lines
))))
155 if not autoescape
or isinstance(value
, SafeData
):
156 for i
, line
in enumerate(lines
):
157 lines
[i
] = (u
"%0" + width
+ u
"d. %s") % (i
+ 1, line
)
159 for i
, line
in enumerate(lines
):
160 lines
[i
] = (u
"%0" + width
+ u
"d. %s") % (i
+ 1, escape(line
))
161 return mark_safe(u
'\n'.join(lines
))
162 linenumbers
.is_safe
= True
163 linenumbers
.needs_autoescape
= True
164 linenumbers
= stringfilter(linenumbers
)
167 """Converts a string into all lowercase."""
170 lower
= stringfilter(lower
)
172 def make_list(value
):
174 Returns the value turned into a list.
176 For an integer, it's a list of digits.
177 For a string, it's a list of characters.
180 make_list
.is_safe
= False
181 make_list
= stringfilter(make_list
)
185 Normalizes string, converts to lowercase, removes non-alpha characters,
186 and converts spaces to hyphens.
189 value
= unicodedata
.normalize('NFKD', value
).encode('ascii', 'ignore')
190 value
= unicode(re
.sub('[^\w\s-]', '', value
).strip().lower())
191 return mark_safe(re
.sub('[-\s]+', '-', value
))
192 slugify
.is_safe
= True
193 slugify
= stringfilter(slugify
)
195 def stringformat(value
, arg
):
197 Formats the variable according to the arg, a string formatting specifier.
199 This specifier uses Python string formating syntax, with the exception that
200 the leading "%" is dropped.
202 See http://docs.python.org/lib/typesseq-strings.html for documentation
203 of Python string formatting
206 return (u
"%" + unicode(arg
)) % value
207 except (ValueError, TypeError):
209 stringformat
.is_safe
= True
212 """Converts a string into titlecase."""
213 return re
.sub("([a-z])'([A-Z])", lambda m
: m
.group(0).lower(), value
.title())
215 title
= stringfilter(title
)
217 def truncatewords(value
, arg
):
219 Truncates a string after a certain number of words.
221 Argument: Number of words to truncate after.
223 from django
.utils
.text
import truncate_words
226 except ValueError: # Invalid literal for int().
227 return value
# Fail silently.
228 return truncate_words(value
, length
)
229 truncatewords
.is_safe
= True
230 truncatewords
= stringfilter(truncatewords
)
232 def truncatewords_html(value
, arg
):
234 Truncates HTML after a certain number of words.
236 Argument: Number of words to truncate after.
238 from django
.utils
.text
import truncate_html_words
241 except ValueError: # invalid literal for int()
242 return value
# Fail silently.
243 return truncate_html_words(value
, length
)
244 truncatewords_html
.is_safe
= True
245 truncatewords_html
= stringfilter(truncatewords_html
)
248 """Converts a string into all uppercase."""
250 upper
.is_safe
= False
251 upper
= stringfilter(upper
)
253 def urlencode(value
):
254 """Escapes a value for use in a URL."""
255 from django
.utils
.http
import urlquote
256 return urlquote(value
)
257 urlencode
.is_safe
= False
258 urlencode
= stringfilter(urlencode
)
260 def urlize(value
, autoescape
=None):
261 """Converts URLs in plain text into clickable links."""
262 from django
.utils
.html
import urlize
263 return mark_safe(urlize(value
, nofollow
=True, autoescape
=autoescape
))
265 urlize
.needs_autoescape
= True
266 urlize
= stringfilter(urlize
)
268 def urlizetrunc(value
, limit
, autoescape
=None):
270 Converts URLs into clickable links, truncating URLs to the given character
271 limit, and adding 'rel=nofollow' attribute to discourage spamming.
273 Argument: Length to truncate URLs to.
275 from django
.utils
.html
import urlize
276 return mark_safe(urlize(value
, trim_url_limit
=int(limit
), nofollow
=True,
277 autoescape
=autoescape
))
278 urlizetrunc
.is_safe
= True
279 urlizetrunc
.needs_autoescape
= True
280 urlizetrunc
= stringfilter(urlizetrunc
)
282 def wordcount(value
):
283 """Returns the number of words."""
284 return len(value
.split())
285 wordcount
.is_safe
= False
286 wordcount
= stringfilter(wordcount
)
288 def wordwrap(value
, arg
):
290 Wraps words at specified line length.
292 Argument: number of characters to wrap the text at.
294 from django
.utils
.text
import wrap
295 return wrap(value
, int(arg
))
296 wordwrap
.is_safe
= True
297 wordwrap
= stringfilter(wordwrap
)
299 def ljust(value
, arg
):
301 Left-aligns the value in a field of a given width.
303 Argument: field size.
305 return value
.ljust(int(arg
))
307 ljust
= stringfilter(ljust
)
309 def rjust(value
, arg
):
311 Right-aligns the value in a field of a given width.
313 Argument: field size.
315 return value
.rjust(int(arg
))
317 rjust
= stringfilter(rjust
)
319 def center(value
, arg
):
320 """Centers the value in a field of a given width."""
321 return value
.center(int(arg
))
322 center
.is_safe
= True
323 center
= stringfilter(center
)
327 Removes all values of arg from the given string.
329 safe
= isinstance(value
, SafeData
)
330 value
= value
.replace(arg
, u
'')
331 if safe
and arg
!= ';':
332 return mark_safe(value
)
334 cut
= stringfilter(cut
)
342 Marks the value as a string that should not be auto-escaped.
344 from django
.utils
.safestring
import mark_for_escaping
345 return mark_for_escaping(value
)
346 escape
.is_safe
= True
347 escape
= stringfilter(escape
)
349 def force_escape(value
):
351 Escapes a string's HTML. This returns a new string containing the escaped
352 characters (as opposed to "escape", which marks the content for later
355 from django
.utils
.html
import escape
356 return mark_safe(escape(value
))
357 force_escape
= stringfilter(force_escape
)
358 force_escape
.is_safe
= True
360 def linebreaks(value
, autoescape
=None):
362 Replaces line breaks in plain text with appropriate HTML; a single
363 newline becomes an HTML line break (``<br />``) and a new line
364 followed by a blank line becomes a paragraph break (``</p>``).
366 from django
.utils
.html
import linebreaks
367 autoescape
= autoescape
and not isinstance(value
, SafeData
)
368 return mark_safe(linebreaks(value
, autoescape
))
369 linebreaks
.is_safe
= True
370 linebreaks
.needs_autoescape
= True
371 linebreaks
= stringfilter(linebreaks
)
373 def linebreaksbr(value
, autoescape
=None):
375 Converts all newlines in a piece of plain text to HTML line breaks
378 if autoescape
and not isinstance(value
, SafeData
):
379 from django
.utils
.html
import escape
380 value
= escape(value
)
381 return mark_safe(value
.replace('\n', '<br />'))
382 linebreaksbr
.is_safe
= True
383 linebreaksbr
.needs_autoescape
= True
384 linebreaksbr
= stringfilter(linebreaksbr
)
388 Marks the value as a string that should not be auto-escaped.
390 from django
.utils
.safestring
import mark_safe
391 return mark_safe(value
)
393 safe
= stringfilter(safe
)
395 def removetags(value
, tags
):
396 """Removes a space separated list of [X]HTML tags from the output."""
397 tags
= [re
.escape(tag
) for tag
in tags
.split()]
398 tags_re
= u
'(%s)' % u
'|'.join(tags
)
399 starttag_re
= re
.compile(ur
'<%s(/?>|(\s+[^>]*>))' % tags_re
, re
.U
)
400 endtag_re
= re
.compile(u
'</%s>' % tags_re
)
401 value
= starttag_re
.sub(u
'', value
)
402 value
= endtag_re
.sub(u
'', value
)
404 removetags
.is_safe
= True
405 removetags
= stringfilter(removetags
)
407 def striptags(value
):
408 """Strips all [X]HTML tags."""
409 from django
.utils
.html
import strip_tags
410 return strip_tags(value
)
411 striptags
.is_safe
= True
412 striptags
= stringfilter(striptags
)
418 def dictsort(value
, arg
):
420 Takes a list of dicts, returns that list sorted by the property given in
423 var_resolve
= Variable(arg
).resolve
424 decorated
= [(var_resolve(item
), item
) for item
in value
]
426 return [item
[1] for item
in decorated
]
427 dictsort
.is_safe
= False
429 def dictsortreversed(value
, arg
):
431 Takes a list of dicts, returns that list sorted in reverse order by the
432 property given in the argument.
434 var_resolve
= Variable(arg
).resolve
435 decorated
= [(var_resolve(item
), item
) for item
in value
]
438 return [item
[1] for item
in decorated
]
439 dictsortreversed
.is_safe
= False
442 """Returns the first item in a list."""
447 first
.is_safe
= False
449 def join(value
, arg
):
450 """Joins a list with a string, like Python's ``str.join(list)``."""
452 data
= arg
.join(map(force_unicode
, value
))
453 except AttributeError: # fail silently but nicely
455 safe_args
= reduce(lambda lhs
, rhs
: lhs
and isinstance(rhs
, SafeData
),
458 return mark_safe(data
)
464 "Returns the last item in a list"
472 """Returns the length of the value - useful for lists."""
474 length
.is_safe
= True
476 def length_is(value
, arg
):
477 """Returns a boolean of whether the value's length is the argument."""
478 return len(value
) == int(arg
)
479 length_is
.is_safe
= True
482 """Returns a random item from the list."""
483 return random_module
.choice(value
)
484 random
.is_safe
= True
486 def slice_(value
, arg
):
488 Returns a slice of the list.
490 Uses the same syntax as Python's list slicing; see
491 http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
496 for x
in arg
.split(u
':'):
501 return value
[slice(*bits
)]
503 except (ValueError, TypeError):
504 return value
# Fail silently.
505 slice_
.is_safe
= True
507 def unordered_list(value
, autoescape
=None):
509 Recursively takes a self-nested list and returns an HTML unordered list --
510 WITHOUT opening and closing <ul> tags.
512 The list is assumed to be in the proper format. For example, if ``var``
513 contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
514 then ``{{ var|unordered_list }}`` would return::
529 from django
.utils
.html
import conditional_escape
530 escaper
= conditional_escape
532 escaper
= lambda x
: x
533 def convert_old_style_list(list_
):
535 Converts old style lists to the new easier to understand format.
537 The old list format looked like:
538 ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
540 And it is converted to:
541 ['Item 1', ['Item 1.1', 'Item 1.2]]
543 if not isinstance(list_
, (tuple, list)) or len(list_
) != 2:
545 first_item
, second_item
= list_
546 if second_item
== []:
547 return [first_item
], True
548 old_style_list
= True
550 for sublist
in second_item
:
551 item
, old_style_list
= convert_old_style_list(sublist
)
552 if not old_style_list
:
554 new_second_item
.extend(item
)
556 second_item
= new_second_item
557 return [first_item
, second_item
], old_style_list
558 def _helper(list_
, tabs
=1):
559 indent
= u
'\t' * tabs
562 list_length
= len(list_
)
564 while i
< list_length
:
568 if isinstance(title
, (list, tuple)):
571 elif i
< list_length
- 1:
572 next_item
= list_
[i
+1]
573 if next_item
and isinstance(next_item
, (list, tuple)):
574 # The next item is a sub-list.
575 sublist_item
= next_item
576 # We've processed the next item now too.
579 sublist
= _helper(sublist_item
, tabs
+1)
580 sublist
= '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent
, sublist
,
582 output
.append('%s<li>%s%s</li>' % (indent
,
583 escaper(force_unicode(title
)), sublist
))
585 return '\n'.join(output
)
586 value
, converted
= convert_old_style_list(value
)
587 return mark_safe(_helper(value
))
588 unordered_list
.is_safe
= True
589 unordered_list
.needs_autoescape
= True
596 """Adds the arg to the value."""
597 return int(value
) + int(arg
)
600 def get_digit(value
, arg
):
602 Given a whole number, returns the requested digit of it, where 1 is the
603 right-most digit, 2 is the second-right-most digit, etc. Returns the
604 original value for invalid input (if input or argument is not an integer,
605 or if argument is less than 1). Otherwise, output is always an integer.
611 return value
# Fail silently for an invalid argument
615 return int(str(value
)[-arg
])
618 get_digit
.is_safe
= False
624 def date(value
, arg
=None):
625 """Formats a date according to the given format."""
626 from django
.utils
.dateformat
import format
630 arg
= settings
.DATE_FORMAT
631 return format(value
, arg
)
634 def time(value
, arg
=None):
635 """Formats a time according to the given format."""
636 from django
.utils
.dateformat
import time_format
637 if value
in (None, u
''):
640 arg
= settings
.TIME_FORMAT
641 return time_format(value
, arg
)
644 def timesince(value
, arg
=None):
645 """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
646 from django
.utils
.timesince
import timesince
651 return timesince(value
, arg
)
652 return timesince(value
)
653 except (ValueError, TypeError):
655 timesince
.is_safe
= False
657 def timeuntil(value
, arg
=None):
658 """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
659 from django
.utils
.timesince
import timeuntil
660 from datetime
import datetime
664 return timeuntil(value
, arg
)
665 except (ValueError, TypeError):
667 timeuntil
.is_safe
= False
673 def default(value
, arg
):
674 """If value is unavailable, use given default."""
676 default
.is_safe
= False
678 def default_if_none(value
, arg
):
679 """If value is None, use given default."""
683 default_if_none
.is_safe
= False
685 def divisibleby(value
, arg
):
686 """Returns True if the value is devisible by the argument."""
687 return int(value
) % int(arg
) == 0
688 divisibleby
.is_safe
= False
690 def yesno(value
, arg
=None):
692 Given a string mapping values for true, false and (optionally) None,
693 returns one of those strings accoding to the value:
695 ========== ====================== ==================================
696 Value Argument Outputs
697 ========== ====================== ==================================
698 ``True`` ``"yeah,no,maybe"`` ``yeah``
699 ``False`` ``"yeah,no,maybe"`` ``no``
700 ``None`` ``"yeah,no,maybe"`` ``maybe``
701 ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
702 if no mapping for None is given.
703 ========== ====================== ==================================
706 arg
= ugettext('yes,no,maybe')
707 bits
= arg
.split(u
',')
709 return value
# Invalid arg.
711 yes
, no
, maybe
= bits
713 # Unpack list of wrong size (no "maybe" value provided).
714 yes
, no
, maybe
= bits
[0], bits
[1], bits
[1]
720 yesno
.is_safe
= False
726 def filesizeformat(bytes
):
728 Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
737 return ungettext("%(size)d byte", "%(size)d bytes", bytes
) % {'size': bytes
}
738 if bytes
< 1024 * 1024:
739 return ugettext("%.1f KB") % (bytes
/ 1024)
740 if bytes
< 1024 * 1024 * 1024:
741 return ugettext("%.1f MB") % (bytes
/ (1024 * 1024))
742 return ugettext("%.1f GB") % (bytes
/ (1024 * 1024 * 1024))
743 filesizeformat
.is_safe
= True
745 def pluralize(value
, arg
=u
's'):
747 Returns a plural suffix if the value is not 1. By default, 's' is used as
750 * If value is 0, vote{{ value|pluralize }} displays "0 votes".
751 * If value is 1, vote{{ value|pluralize }} displays "1 vote".
752 * If value is 2, vote{{ value|pluralize }} displays "2 votes".
754 If an argument is provided, that string is used instead:
756 * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
757 * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
758 * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
760 If the provided argument contains a comma, the text before the comma is
761 used for the singular case and the text after the comma is used for the
764 * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
765 * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
766 * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
770 bits
= arg
.split(u
',')
773 singular_suffix
, plural_suffix
= bits
[:2]
778 except ValueError: # Invalid string that's not a number.
780 except TypeError: # Value isn't a string or a number; maybe it's a list?
784 except TypeError: # len() of unsized object.
786 return singular_suffix
787 pluralize
.is_safe
= False
789 def phone2numeric(value
):
790 """Takes a phone number and converts it in to its numerical equivalent."""
791 from django
.utils
.text
import phone2numeric
792 return phone2numeric(value
)
793 phone2numeric
.is_safe
= True
796 """A wrapper around pprint.pprint -- for debugging, really."""
797 from pprint
import pformat
799 return pformat(value
)
801 return u
"Error in formatting: %s" % force_unicode(e
, errors
="replace")
802 pprint
.is_safe
= True
804 # Syntax: register.filter(name of filter, callback)
806 register
.filter(addslashes
)
807 register
.filter(capfirst
)
808 register
.filter(center
)
810 register
.filter(date
)
811 register
.filter(default
)
812 register
.filter(default_if_none
)
813 register
.filter(dictsort
)
814 register
.filter(dictsortreversed
)
815 register
.filter(divisibleby
)
816 register
.filter(escape
)
817 register
.filter(escapejs
)
818 register
.filter(filesizeformat
)
819 register
.filter(first
)
820 register
.filter(fix_ampersands
)
821 register
.filter(floatformat
)
822 register
.filter(force_escape
)
823 register
.filter(get_digit
)
824 register
.filter(iriencode
)
825 register
.filter(join
)
826 register
.filter(last
)
827 register
.filter(length
)
828 register
.filter(length_is
)
829 register
.filter(linebreaks
)
830 register
.filter(linebreaksbr
)
831 register
.filter(linenumbers
)
832 register
.filter(ljust
)
833 register
.filter(lower
)
834 register
.filter(make_list
)
835 register
.filter(phone2numeric
)
836 register
.filter(pluralize
)
837 register
.filter(pprint
)
838 register
.filter(removetags
)
839 register
.filter(random
)
840 register
.filter(rjust
)
841 register
.filter(safe
)
842 register
.filter('slice', slice_
)
843 register
.filter(slugify
)
844 register
.filter(stringformat
)
845 register
.filter(striptags
)
846 register
.filter(time
)
847 register
.filter(timesince
)
848 register
.filter(timeuntil
)
849 register
.filter(title
)
850 register
.filter(truncatewords
)
851 register
.filter(truncatewords_html
)
852 register
.filter(unordered_list
)
853 register
.filter(upper
)
854 register
.filter(urlencode
)
855 register
.filter(urlize
)
856 register
.filter(urlizetrunc
)
857 register
.filter(wordcount
)
858 register
.filter(wordwrap
)
859 register
.filter(yesno
)