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
)
78 """Backslash-escapes characters for use in JavaScript strings."""
79 for bad
, good
in _js_escapes
:
80 value
= value
.replace(bad
, good
)
82 escapejs
= stringfilter(escapejs
)
84 def fix_ampersands(value
):
85 """Replaces ampersands with ``&`` entities."""
86 from django
.utils
.html
import fix_ampersands
87 return fix_ampersands(value
)
88 fix_ampersands
.is_safe
=True
89 fix_ampersands
= stringfilter(fix_ampersands
)
91 def floatformat(text
, arg
=-1):
93 Displays a float to a specified number of decimal places.
95 If called without an argument, it displays the floating point number with
96 one decimal place -- but only if there's a decimal place to be displayed:
101 * {{ num1|floatformat }} displays "34.2"
102 * {{ num2|floatformat }} displays "34"
103 * {{ num3|floatformat }} displays "34.3"
105 If arg is positive, it will always display exactly arg number of decimal
108 * {{ num1|floatformat:3 }} displays "34.232"
109 * {{ num2|floatformat:3 }} displays "34.000"
110 * {{ num3|floatformat:3 }} displays "34.260"
112 If arg is negative, it will display arg number of decimal places -- but
113 only if there are places to be displayed:
115 * {{ num1|floatformat:"-3" }} displays "34.232"
116 * {{ num2|floatformat:"-3" }} displays "34"
117 * {{ num3|floatformat:"-3" }} displays "34.260"
121 except (ValueError, TypeError):
126 return force_unicode(f
)
129 except OverflowError:
130 return force_unicode(f
)
132 return mark_safe(u
'%d' % int(f
))
134 formatstr
= u
'%%.%df' % abs(d
)
135 return mark_safe(formatstr
% f
)
136 floatformat
.is_safe
= True
138 def iriencode(value
):
139 """Escapes an IRI value for use in a URL."""
140 return force_unicode(iri_to_uri(value
))
141 iriencode
.is_safe
= True
142 iriencode
= stringfilter(iriencode
)
144 def linenumbers(value
, autoescape
=None):
145 """Displays text with line numbers."""
146 from django
.utils
.html
import escape
147 lines
= value
.split(u
'\n')
148 # Find the maximum width of the line count, for use with zero padding
149 # string format command
150 width
= unicode(len(unicode(len(lines
))))
151 if not autoescape
or isinstance(value
, SafeData
):
152 for i
, line
in enumerate(lines
):
153 lines
[i
] = (u
"%0" + width
+ u
"d. %s") % (i
+ 1, line
)
155 for i
, line
in enumerate(lines
):
156 lines
[i
] = (u
"%0" + width
+ u
"d. %s") % (i
+ 1, escape(line
))
157 return mark_safe(u
'\n'.join(lines
))
158 linenumbers
.is_safe
= True
159 linenumbers
.needs_autoescape
= True
160 linenumbers
= stringfilter(linenumbers
)
163 """Converts a string into all lowercase."""
166 lower
= stringfilter(lower
)
168 def make_list(value
):
170 Returns the value turned into a list.
172 For an integer, it's a list of digits.
173 For a string, it's a list of characters.
176 make_list
.is_safe
= False
177 make_list
= stringfilter(make_list
)
181 Normalizes string, converts to lowercase, removes non-alpha characters,
182 and converts spaces to hyphens.
185 value
= unicodedata
.normalize('NFKD', value
).encode('ascii', 'ignore')
186 value
= unicode(re
.sub('[^\w\s-]', '', value
).strip().lower())
187 return mark_safe(re
.sub('[-\s]+', '-', value
))
188 slugify
.is_safe
= True
189 slugify
= stringfilter(slugify
)
191 def stringformat(value
, arg
):
193 Formats the variable according to the arg, a string formatting specifier.
195 This specifier uses Python string formating syntax, with the exception that
196 the leading "%" is dropped.
198 See http://docs.python.org/lib/typesseq-strings.html for documentation
199 of Python string formatting
202 return (u
"%" + unicode(arg
)) % value
203 except (ValueError, TypeError):
205 stringformat
.is_safe
= True
208 """Converts a string into titlecase."""
209 return re
.sub("([a-z])'([A-Z])", lambda m
: m
.group(0).lower(), value
.title())
211 title
= stringfilter(title
)
213 def truncatewords(value
, arg
):
215 Truncates a string after a certain number of words.
217 Argument: Number of words to truncate after.
219 from django
.utils
.text
import truncate_words
222 except ValueError: # Invalid literal for int().
223 return value
# Fail silently.
224 return truncate_words(value
, length
)
225 truncatewords
.is_safe
= True
226 truncatewords
= stringfilter(truncatewords
)
228 def truncatewords_html(value
, arg
):
230 Truncates HTML after a certain number of words.
232 Argument: Number of words to truncate after.
234 from django
.utils
.text
import truncate_html_words
237 except ValueError: # invalid literal for int()
238 return value
# Fail silently.
239 return truncate_html_words(value
, length
)
240 truncatewords_html
.is_safe
= True
241 truncatewords_html
= stringfilter(truncatewords_html
)
244 """Converts a string into all uppercase."""
246 upper
.is_safe
= False
247 upper
= stringfilter(upper
)
249 def urlencode(value
):
250 """Escapes a value for use in a URL."""
251 from django
.utils
.http
import urlquote
252 return urlquote(value
)
253 urlencode
.is_safe
= False
254 urlencode
= stringfilter(urlencode
)
256 def urlize(value
, autoescape
=None):
257 """Converts URLs in plain text into clickable links."""
258 from django
.utils
.html
import urlize
259 return mark_safe(urlize(value
, nofollow
=True, autoescape
=autoescape
))
261 urlize
.needs_autoescape
= True
262 urlize
= stringfilter(urlize
)
264 def urlizetrunc(value
, limit
, autoescape
=None):
266 Converts URLs into clickable links, truncating URLs to the given character
267 limit, and adding 'rel=nofollow' attribute to discourage spamming.
269 Argument: Length to truncate URLs to.
271 from django
.utils
.html
import urlize
272 return mark_safe(urlize(value
, trim_url_limit
=int(limit
), nofollow
=True,
273 autoescape
=autoescape
))
274 urlizetrunc
.is_safe
= True
275 urlizetrunc
.needs_autoescape
= True
276 urlizetrunc
= stringfilter(urlizetrunc
)
278 def wordcount(value
):
279 """Returns the number of words."""
280 return len(value
.split())
281 wordcount
.is_safe
= False
282 wordcount
= stringfilter(wordcount
)
284 def wordwrap(value
, arg
):
286 Wraps words at specified line length.
288 Argument: number of characters to wrap the text at.
290 from django
.utils
.text
import wrap
291 return wrap(value
, int(arg
))
292 wordwrap
.is_safe
= True
293 wordwrap
= stringfilter(wordwrap
)
295 def ljust(value
, arg
):
297 Left-aligns the value in a field of a given width.
299 Argument: field size.
301 return value
.ljust(int(arg
))
303 ljust
= stringfilter(ljust
)
305 def rjust(value
, arg
):
307 Right-aligns the value in a field of a given width.
309 Argument: field size.
311 return value
.rjust(int(arg
))
313 rjust
= stringfilter(rjust
)
315 def center(value
, arg
):
316 """Centers the value in a field of a given width."""
317 return value
.center(int(arg
))
318 center
.is_safe
= True
319 center
= stringfilter(center
)
323 Removes all values of arg from the given string.
325 safe
= isinstance(value
, SafeData
)
326 value
= value
.replace(arg
, u
'')
327 if safe
and arg
!= ';':
328 return mark_safe(value
)
330 cut
= stringfilter(cut
)
338 Marks the value as a string that should not be auto-escaped.
340 from django
.utils
.safestring
import mark_for_escaping
341 return mark_for_escaping(value
)
342 escape
.is_safe
= True
343 escape
= stringfilter(escape
)
345 def force_escape(value
):
347 Escapes a string's HTML. This returns a new string containing the escaped
348 characters (as opposed to "escape", which marks the content for later
351 from django
.utils
.html
import escape
352 return mark_safe(escape(value
))
353 force_escape
= stringfilter(force_escape
)
354 force_escape
.is_safe
= True
356 def linebreaks(value
, autoescape
=None):
358 Replaces line breaks in plain text with appropriate HTML; a single
359 newline becomes an HTML line break (``<br />``) and a new line
360 followed by a blank line becomes a paragraph break (``</p>``).
362 from django
.utils
.html
import linebreaks
363 autoescape
= autoescape
and not isinstance(value
, SafeData
)
364 return mark_safe(linebreaks(value
, autoescape
))
365 linebreaks
.is_safe
= True
366 linebreaks
.needs_autoescape
= True
367 linebreaks
= stringfilter(linebreaks
)
369 def linebreaksbr(value
, autoescape
=None):
371 Converts all newlines in a piece of plain text to HTML line breaks
374 if autoescape
and not isinstance(value
, SafeData
):
375 from django
.utils
.html
import escape
376 value
= escape(value
)
377 return mark_safe(value
.replace('\n', '<br />'))
378 linebreaksbr
.is_safe
= True
379 linebreaksbr
.needs_autoescape
= True
380 linebreaksbr
= stringfilter(linebreaksbr
)
384 Marks the value as a string that should not be auto-escaped.
386 from django
.utils
.safestring
import mark_safe
387 return mark_safe(value
)
389 safe
= stringfilter(safe
)
391 def removetags(value
, tags
):
392 """Removes a space separated list of [X]HTML tags from the output."""
393 tags
= [re
.escape(tag
) for tag
in tags
.split()]
394 tags_re
= u
'(%s)' % u
'|'.join(tags
)
395 starttag_re
= re
.compile(ur
'<%s(/?>|(\s+[^>]*>))' % tags_re
, re
.U
)
396 endtag_re
= re
.compile(u
'</%s>' % tags_re
)
397 value
= starttag_re
.sub(u
'', value
)
398 value
= endtag_re
.sub(u
'', value
)
400 removetags
.is_safe
= True
401 removetags
= stringfilter(removetags
)
403 def striptags(value
):
404 """Strips all [X]HTML tags."""
405 from django
.utils
.html
import strip_tags
406 return strip_tags(value
)
407 striptags
.is_safe
= True
408 striptags
= stringfilter(striptags
)
414 def dictsort(value
, arg
):
416 Takes a list of dicts, returns that list sorted by the property given in
419 var_resolve
= Variable(arg
).resolve
420 decorated
= [(var_resolve(item
), item
) for item
in value
]
422 return [item
[1] for item
in decorated
]
423 dictsort
.is_safe
= False
425 def dictsortreversed(value
, arg
):
427 Takes a list of dicts, returns that list sorted in reverse order by the
428 property given in the argument.
430 var_resolve
= Variable(arg
).resolve
431 decorated
= [(var_resolve(item
), item
) for item
in value
]
434 return [item
[1] for item
in decorated
]
435 dictsortreversed
.is_safe
= False
438 """Returns the first item in a list."""
443 first
.is_safe
= False
445 def join(value
, arg
):
446 """Joins a list with a string, like Python's ``str.join(list)``."""
448 data
= arg
.join(map(force_unicode
, value
))
449 except AttributeError: # fail silently but nicely
451 safe_args
= reduce(lambda lhs
, rhs
: lhs
and isinstance(rhs
, SafeData
),
454 return mark_safe(data
)
460 "Returns the last item in a list"
468 """Returns the length of the value - useful for lists."""
470 length
.is_safe
= True
472 def length_is(value
, arg
):
473 """Returns a boolean of whether the value's length is the argument."""
474 return len(value
) == int(arg
)
475 length_is
.is_safe
= True
478 """Returns a random item from the list."""
479 return random_module
.choice(value
)
480 random
.is_safe
= True
482 def slice_(value
, arg
):
484 Returns a slice of the list.
486 Uses the same syntax as Python's list slicing; see
487 http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
492 for x
in arg
.split(u
':'):
497 return value
[slice(*bits
)]
499 except (ValueError, TypeError):
500 return value
# Fail silently.
501 slice_
.is_safe
= True
503 def unordered_list(value
, autoescape
=None):
505 Recursively takes a self-nested list and returns an HTML unordered list --
506 WITHOUT opening and closing <ul> tags.
508 The list is assumed to be in the proper format. For example, if ``var``
509 contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
510 then ``{{ var|unordered_list }}`` would return::
525 from django
.utils
.html
import conditional_escape
526 escaper
= conditional_escape
528 escaper
= lambda x
: x
529 def convert_old_style_list(list_
):
531 Converts old style lists to the new easier to understand format.
533 The old list format looked like:
534 ['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
536 And it is converted to:
537 ['Item 1', ['Item 1.1', 'Item 1.2]]
539 if not isinstance(list_
, (tuple, list)) or len(list_
) != 2:
541 first_item
, second_item
= list_
542 if second_item
== []:
543 return [first_item
], True
544 old_style_list
= True
546 for sublist
in second_item
:
547 item
, old_style_list
= convert_old_style_list(sublist
)
548 if not old_style_list
:
550 new_second_item
.extend(item
)
552 second_item
= new_second_item
553 return [first_item
, second_item
], old_style_list
554 def _helper(list_
, tabs
=1):
555 indent
= u
'\t' * tabs
558 list_length
= len(list_
)
560 while i
< list_length
:
564 if isinstance(title
, (list, tuple)):
567 elif i
< list_length
- 1:
568 next_item
= list_
[i
+1]
569 if next_item
and isinstance(next_item
, (list, tuple)):
570 # The next item is a sub-list.
571 sublist_item
= next_item
572 # We've processed the next item now too.
575 sublist
= _helper(sublist_item
, tabs
+1)
576 sublist
= '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent
, sublist
,
578 output
.append('%s<li>%s%s</li>' % (indent
,
579 escaper(force_unicode(title
)), sublist
))
581 return '\n'.join(output
)
582 value
, converted
= convert_old_style_list(value
)
583 return mark_safe(_helper(value
))
584 unordered_list
.is_safe
= True
585 unordered_list
.needs_autoescape
= True
592 """Adds the arg to the value."""
593 return int(value
) + int(arg
)
596 def get_digit(value
, arg
):
598 Given a whole number, returns the requested digit of it, where 1 is the
599 right-most digit, 2 is the second-right-most digit, etc. Returns the
600 original value for invalid input (if input or argument is not an integer,
601 or if argument is less than 1). Otherwise, output is always an integer.
607 return value
# Fail silently for an invalid argument
611 return int(str(value
)[-arg
])
614 get_digit
.is_safe
= False
620 def date(value
, arg
=None):
621 """Formats a date according to the given format."""
622 from django
.utils
.dateformat
import format
626 arg
= settings
.DATE_FORMAT
627 return format(value
, arg
)
630 def time(value
, arg
=None):
631 """Formats a time according to the given format."""
632 from django
.utils
.dateformat
import time_format
633 if value
in (None, u
''):
636 arg
= settings
.TIME_FORMAT
637 return time_format(value
, arg
)
640 def timesince(value
, arg
=None):
641 """Formats a date as the time since that date (i.e. "4 days, 6 hours")."""
642 from django
.utils
.timesince
import timesince
646 return timesince(arg
, value
)
647 return timesince(value
)
648 timesince
.is_safe
= False
650 def timeuntil(value
, arg
=None):
651 """Formats a date as the time until that date (i.e. "4 days, 6 hours")."""
652 from django
.utils
.timesince
import timesince
653 from datetime
import datetime
657 return timesince(arg
, value
)
658 return timesince(datetime
.now(), value
)
659 timeuntil
.is_safe
= False
665 def default(value
, arg
):
666 """If value is unavailable, use given default."""
668 default
.is_safe
= False
670 def default_if_none(value
, arg
):
671 """If value is None, use given default."""
675 default_if_none
.is_safe
= False
677 def divisibleby(value
, arg
):
678 """Returns True if the value is devisible by the argument."""
679 return int(value
) % int(arg
) == 0
680 divisibleby
.is_safe
= False
682 def yesno(value
, arg
=None):
684 Given a string mapping values for true, false and (optionally) None,
685 returns one of those strings accoding to the value:
687 ========== ====================== ==================================
688 Value Argument Outputs
689 ========== ====================== ==================================
690 ``True`` ``"yeah,no,maybe"`` ``yeah``
691 ``False`` ``"yeah,no,maybe"`` ``no``
692 ``None`` ``"yeah,no,maybe"`` ``maybe``
693 ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
694 if no mapping for None is given.
695 ========== ====================== ==================================
698 arg
= ugettext('yes,no,maybe')
699 bits
= arg
.split(u
',')
701 return value
# Invalid arg.
703 yes
, no
, maybe
= bits
705 # Unpack list of wrong size (no "maybe" value provided).
706 yes
, no
, maybe
= bits
[0], bits
[1], bits
[1]
712 yesno
.is_safe
= False
718 def filesizeformat(bytes
):
720 Formats the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
729 return ungettext("%(size)d byte", "%(size)d bytes", bytes
) % {'size': bytes
}
730 if bytes
< 1024 * 1024:
731 return ugettext("%.1f KB") % (bytes
/ 1024)
732 if bytes
< 1024 * 1024 * 1024:
733 return ugettext("%.1f MB") % (bytes
/ (1024 * 1024))
734 return ugettext("%.1f GB") % (bytes
/ (1024 * 1024 * 1024))
735 filesizeformat
.is_safe
= True
737 def pluralize(value
, arg
=u
's'):
739 Returns a plural suffix if the value is not 1. By default, 's' is used as
742 * If value is 0, vote{{ value|pluralize }} displays "0 votes".
743 * If value is 1, vote{{ value|pluralize }} displays "1 vote".
744 * If value is 2, vote{{ value|pluralize }} displays "2 votes".
746 If an argument is provided, that string is used instead:
748 * If value is 0, class{{ value|pluralize:"es" }} displays "0 classes".
749 * If value is 1, class{{ value|pluralize:"es" }} displays "1 class".
750 * If value is 2, class{{ value|pluralize:"es" }} displays "2 classes".
752 If the provided argument contains a comma, the text before the comma is
753 used for the singular case and the text after the comma is used for the
756 * If value is 0, cand{{ value|pluralize:"y,ies" }} displays "0 candies".
757 * If value is 1, cand{{ value|pluralize:"y,ies" }} displays "1 candy".
758 * If value is 2, cand{{ value|pluralize:"y,ies" }} displays "2 candies".
762 bits
= arg
.split(u
',')
765 singular_suffix
, plural_suffix
= bits
[:2]
770 except ValueError: # Invalid string that's not a number.
772 except TypeError: # Value isn't a string or a number; maybe it's a list?
776 except TypeError: # len() of unsized object.
778 return singular_suffix
779 pluralize
.is_safe
= False
781 def phone2numeric(value
):
782 """Takes a phone number and converts it in to its numerical equivalent."""
783 from django
.utils
.text
import phone2numeric
784 return phone2numeric(value
)
785 phone2numeric
.is_safe
= True
788 """A wrapper around pprint.pprint -- for debugging, really."""
789 from pprint
import pformat
791 return pformat(value
)
793 return u
"Error in formatting: %s" % force_unicode(e
, errors
="replace")
794 pprint
.is_safe
= True
796 # Syntax: register.filter(name of filter, callback)
798 register
.filter(addslashes
)
799 register
.filter(capfirst
)
800 register
.filter(center
)
802 register
.filter(date
)
803 register
.filter(default
)
804 register
.filter(default_if_none
)
805 register
.filter(dictsort
)
806 register
.filter(dictsortreversed
)
807 register
.filter(divisibleby
)
808 register
.filter(escape
)
809 register
.filter(escapejs
)
810 register
.filter(filesizeformat
)
811 register
.filter(first
)
812 register
.filter(fix_ampersands
)
813 register
.filter(floatformat
)
814 register
.filter(force_escape
)
815 register
.filter(get_digit
)
816 register
.filter(iriencode
)
817 register
.filter(join
)
818 register
.filter(last
)
819 register
.filter(length
)
820 register
.filter(length_is
)
821 register
.filter(linebreaks
)
822 register
.filter(linebreaksbr
)
823 register
.filter(linenumbers
)
824 register
.filter(ljust
)
825 register
.filter(lower
)
826 register
.filter(make_list
)
827 register
.filter(phone2numeric
)
828 register
.filter(pluralize
)
829 register
.filter(pprint
)
830 register
.filter(removetags
)
831 register
.filter(random
)
832 register
.filter(rjust
)
833 register
.filter(safe
)
834 register
.filter('slice', slice_
)
835 register
.filter(slugify
)
836 register
.filter(stringformat
)
837 register
.filter(striptags
)
838 register
.filter(time
)
839 register
.filter(timesince
)
840 register
.filter(timeuntil
)
841 register
.filter(title
)
842 register
.filter(truncatewords
)
843 register
.filter(truncatewords_html
)
844 register
.filter(unordered_list
)
845 register
.filter(upper
)
846 register
.filter(urlencode
)
847 register
.filter(urlize
)
848 register
.filter(urlizetrunc
)
849 register
.filter(wordcount
)
850 register
.filter(wordwrap
)
851 register
.filter(yesno
)