Ticket #17135: 17135.2.patch
File 17135.2.patch, 12.5 KB (added by , 13 years ago) |
---|
-
docs/howto/custom-template-tags.txt
143 143 If you leave off the ``name`` argument, as in the second example above, Django 144 144 will use the function's name as the filter name. 145 145 146 Template filters that expect strings147 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~148 149 If you're writing a template filter that only expects a string as the first150 argument, you should use the decorator ``stringfilter``. This will151 convert an object to its string value before being passed to your function:152 153 .. code-block:: python154 155 from django import template156 from django.template.defaultfilters import stringfilter157 158 register = template.Library()159 160 @register.filter161 @stringfilter162 def lower(value):163 return value.lower()164 165 This way, you'll be able to pass, say, an integer to this filter, and it166 won't cause an ``AttributeError`` (because integers don't have ``lower()``167 methods).168 169 146 Filters and auto-escaping 170 147 ~~~~~~~~~~~~~~~~~~~~~~~~~ 171 148 … … 324 301 handle the auto-escaping issues and return a safe string, the 325 302 ``is_safe`` attribute won't change anything either way. 326 303 304 Template filters that expect strings 305 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 306 307 If you're writing a template filter that only expects a string as the first 308 argument, you should use the decorator ``stringfilter``. This will 309 convert an object to its string value before being passed to your function: 310 311 .. code-block:: python 312 313 from django import template 314 from django.template.defaultfilters import stringfilter 315 316 register = template.Library() 317 318 @register.filter 319 @stringfilter 320 def lower(value): 321 return value.lower() 322 323 This way, you'll be able to pass, say, an integer to this filter, and it 324 won't cause an ``AttributeError`` (because integers don't have ``lower()`` 325 methods). 326 327 If you want to set the ``is_safe`` or ``needs_autoescape`` attributes on your 328 filter, pass them as keyword arguments to ``stringfilter``, like this:: 329 330 @register.filter 331 @stringfilter(is_safe=True) 332 def lower(value): 333 return value.lower() 334 335 .. warning:: 336 337 Setting ``is_safe`` or ``needs_autoescape`` directly on a function decorated 338 by ``stringfilter`` does not work. 339 327 340 Writing custom template tags 328 341 ---------------------------- 329 342 -
django/template/defaultfilters.py
27 27 # STRING DECORATOR # 28 28 ####################### 29 29 30 def stringfilter(func ):30 def stringfilter(func=None, **flags): 31 31 """ 32 32 Decorator for filters which should only receive unicode objects. The object 33 33 passed as the first positional argument will be converted to a unicode 34 34 object. 35 35 """ 36 if func is None: 37 # @stringfilter(is_safe=..., needs_autoescape=...) 38 return lambda f: _stringfilter(f, **flags) 39 else: 40 # @stringfilter 41 return _stringfilter(func) 42 43 def _stringfilter(func, **flags): 36 44 def _dec(*args, **kwargs): 37 45 if args: 38 46 args = list(args) … … 45 53 # arguments by the template parser). 46 54 _dec._decorated_function = getattr(func, '_decorated_function', func) 47 55 for attr in ('is_safe', 'needs_autoescape'): 48 if hasattr(func, attr): 56 if attr in flags: 57 setattr(func, attr, flags[attr]) 58 setattr(_dec, attr, flags[attr]) 59 elif hasattr(func, attr): 49 60 setattr(_dec, attr, getattr(func, attr)) 50 61 return wraps(func)(_dec) 51 62 … … 54 65 ################### 55 66 56 67 @register.filter 57 @stringfilter 68 @stringfilter(is_safe=True) 58 69 def addslashes(value): 59 70 """ 60 71 Adds slashes before quotes. Useful for escaping strings in CSV, for … … 62 73 filter instead. 63 74 """ 64 75 return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") 65 addslashes.is_safe = True66 76 67 77 @register.filter 68 @stringfilter 78 @stringfilter(is_safe=True) 69 79 def capfirst(value): 70 80 """Capitalizes the first character of the value.""" 71 81 return value and value[0].upper() + value[1:] 72 capfirst.is_safe = True73 82 74 83 @register.filter("escapejs") 75 84 @stringfilter … … 78 87 return escapejs(value) 79 88 80 89 @register.filter("fix_ampersands") 81 @stringfilter 90 @stringfilter(is_safe=True) 82 91 def fix_ampersands_filter(value): 83 92 """Replaces ampersands with ``&`` entities.""" 84 93 return fix_ampersands(value) 85 fix_ampersands_filter.is_safe = True86 94 87 95 # Values for testing floatformat input against infinity and NaN representations, 88 96 # which differ across platforms and Python versions. Some (i.e. old Windows … … 175 183 floatformat.is_safe = True 176 184 177 185 @register.filter 178 @stringfilter 186 @stringfilter(is_safe=True) 179 187 def iriencode(value): 180 188 """Escapes an IRI value for use in a URL.""" 181 189 return force_unicode(iri_to_uri(value)) 182 iriencode.is_safe = True183 190 184 191 @register.filter 185 @stringfilter 192 @stringfilter(is_safe=True, needs_autoescape=True) 186 193 def linenumbers(value, autoescape=None): 187 194 """Displays text with line numbers.""" 188 195 lines = value.split(u'\n') … … 196 203 for i, line in enumerate(lines): 197 204 lines[i] = (u"%0" + width + u"d. %s") % (i + 1, escape(line)) 198 205 return mark_safe(u'\n'.join(lines)) 199 linenumbers.is_safe = True200 linenumbers.needs_autoescape = True201 206 202 207 @register.filter 203 @stringfilter 208 @stringfilter(is_safe=True) 204 209 def lower(value): 205 210 """Converts a string into all lowercase.""" 206 211 return value.lower() 207 lower.is_safe = True208 212 209 213 @register.filter 210 @stringfilter 214 @stringfilter(is_safe=False) 211 215 def make_list(value): 212 216 """ 213 217 Returns the value turned into a list. … … 216 220 For a string, it's a list of characters. 217 221 """ 218 222 return list(value) 219 make_list.is_safe = False220 223 221 224 @register.filter 222 @stringfilter 225 @stringfilter(is_safe=True) 223 226 def slugify(value): 224 227 """ 225 228 Normalizes string, converts to lowercase, removes non-alpha characters, … … 228 231 value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') 229 232 value = unicode(re.sub('[^\w\s-]', '', value).strip().lower()) 230 233 return mark_safe(re.sub('[-\s]+', '-', value)) 231 slugify.is_safe = True232 234 233 235 @register.filter 234 236 def stringformat(value, arg): … … 248 250 stringformat.is_safe = True 249 251 250 252 @register.filter 251 @stringfilter 253 @stringfilter(is_safe=True) 252 254 def title(value): 253 255 """Converts a string into titlecase.""" 254 256 t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title()) 255 257 return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t) 256 title.is_safe = True257 258 258 259 @register.filter 259 @stringfilter 260 @stringfilter(is_safe=True) 260 261 def truncatechars(value, arg): 261 262 """ 262 263 Truncates a string after a certain number of characters. … … 268 269 except ValueError: # Invalid literal for int(). 269 270 return value # Fail silently. 270 271 return Truncator(value).chars(length) 271 truncatechars.is_safe = True272 272 273 273 @register.filter 274 @stringfilter 274 @stringfilter(is_safe=True) 275 275 def truncatewords(value, arg): 276 276 """ 277 277 Truncates a string after a certain number of words. … … 285 285 except ValueError: # Invalid literal for int(). 286 286 return value # Fail silently. 287 287 return Truncator(value).words(length, truncate=' ...') 288 truncatewords.is_safe = True289 288 290 289 @register.filter 291 @stringfilter 290 @stringfilter(is_safe=True) 292 291 def truncatewords_html(value, arg): 293 292 """ 294 293 Truncates HTML after a certain number of words. … … 302 301 except ValueError: # invalid literal for int() 303 302 return value # Fail silently. 304 303 return Truncator(value).words(length, html=True, truncate=' ...') 305 truncatewords_html.is_safe = True306 304 307 305 @register.filter 308 @stringfilter 306 @stringfilter(is_safe=False) 309 307 def upper(value): 310 308 """Converts a string into all uppercase.""" 311 309 return value.upper() 312 upper.is_safe = False313 310 314 311 @register.filter 315 @stringfilter 312 @stringfilter(is_safe=False) 316 313 def urlencode(value, safe=None): 317 314 """ 318 315 Escapes a value for use in a URL. … … 326 323 if safe is not None: 327 324 kwargs['safe'] = safe 328 325 return urlquote(value, **kwargs) 329 urlencode.is_safe = False330 326 331 327 @register.filter 332 @stringfilter 328 @stringfilter(is_safe=True, needs_autoescape=True) 333 329 def urlize(value, autoescape=None): 334 330 """Converts URLs in plain text into clickable links.""" 335 331 return mark_safe(urlize_impl(value, nofollow=True, autoescape=autoescape)) 336 urlize.is_safe = True337 urlize.needs_autoescape = True338 332 339 333 @register.filter 340 @stringfilter 334 @stringfilter(is_safe=True, needs_autoescape=True) 341 335 def urlizetrunc(value, limit, autoescape=None): 342 336 """ 343 337 Converts URLs into clickable links, truncating URLs to the given character … … 347 341 """ 348 342 return mark_safe(urlize_impl(value, trim_url_limit=int(limit), nofollow=True, 349 343 autoescape=autoescape)) 350 urlizetrunc.is_safe = True351 urlizetrunc.needs_autoescape = True352 344 353 345 @register.filter 354 @stringfilter 346 @stringfilter(is_safe=False) 355 347 def wordcount(value): 356 348 """Returns the number of words.""" 357 349 return len(value.split()) 358 wordcount.is_safe = False359 350 360 351 @register.filter 361 @stringfilter 352 @stringfilter(is_safe=True) 362 353 def wordwrap(value, arg): 363 354 """ 364 355 Wraps words at specified line length. … … 366 357 Argument: number of characters to wrap the text at. 367 358 """ 368 359 return wrap(value, int(arg)) 369 wordwrap.is_safe = True370 360 371 361 @register.filter 372 @stringfilter 362 @stringfilter(is_safe=True) 373 363 def ljust(value, arg): 374 364 """ 375 365 Left-aligns the value in a field of a given width. … … 377 367 Argument: field size. 378 368 """ 379 369 return value.ljust(int(arg)) 380 ljust.is_safe = True381 370 382 371 @register.filter 383 @stringfilter 372 @stringfilter(is_safe=True) 384 373 def rjust(value, arg): 385 374 """ 386 375 Right-aligns the value in a field of a given width. … … 388 377 Argument: field size. 389 378 """ 390 379 return value.rjust(int(arg)) 391 rjust.is_safe = True392 380 393 381 @register.filter 394 @stringfilter 382 @stringfilter(is_safe=True) 395 383 def center(value, arg): 396 384 """Centers the value in a field of a given width.""" 397 385 return value.center(int(arg)) 398 center.is_safe = True399 386 400 387 @register.filter 401 388 @stringfilter … … 414 401 ################### 415 402 416 403 @register.filter("escape") 417 @stringfilter 404 @stringfilter(is_safe=True) 418 405 def escape_filter(value): 419 406 """ 420 407 Marks the value as a string that should not be auto-escaped. 421 408 """ 422 409 return mark_for_escaping(value) 423 escape_filter.is_safe = True424 410 425 411 @register.filter 426 @stringfilter 412 @stringfilter(is_safe=True) 427 413 def force_escape(value): 428 414 """ 429 415 Escapes a string's HTML. This returns a new string containing the escaped … … 431 417 possible escaping). 432 418 """ 433 419 return mark_safe(escape(value)) 434 force_escape.is_safe = True435 420 436 421 @register.filter("linebreaks") 437 @stringfilter 422 @stringfilter(is_safe=True, needs_autoescape=True) 438 423 def linebreaks_filter(value, autoescape=None): 439 424 """ 440 425 Replaces line breaks in plain text with appropriate HTML; a single … … 443 428 """ 444 429 autoescape = autoescape and not isinstance(value, SafeData) 445 430 return mark_safe(linebreaks(value, autoescape)) 446 linebreaks_filter.is_safe = True447 linebreaks_filter.needs_autoescape = True448 431 449 432 @register.filter 450 @stringfilter 433 @stringfilter(is_safe=True, needs_autoescape=True) 451 434 def linebreaksbr(value, autoescape=None): 452 435 """ 453 436 Converts all newlines in a piece of plain text to HTML line breaks … … 458 441 if autoescape: 459 442 value = escape(value) 460 443 return mark_safe(value.replace('\n', '<br />')) 461 linebreaksbr.is_safe = True462 linebreaksbr.needs_autoescape = True463 444 464 445 @register.filter 465 @stringfilter 446 @stringfilter(is_safe=True) 466 447 def safe(value): 467 448 """ 468 449 Marks the value as a string that should not be auto-escaped. 469 450 """ 470 451 return mark_safe(value) 471 safe.is_safe = True472 452 473 453 @register.filter 474 454 def safeseq(value): … … 481 461 safeseq.is_safe = True 482 462 483 463 @register.filter 484 @stringfilter 464 @stringfilter(is_safe=True) 485 465 def removetags(value, tags): 486 466 """Removes a space separated list of [X]HTML tags from the output.""" 487 467 tags = [re.escape(tag) for tag in tags.split()] … … 491 471 value = starttag_re.sub(u'', value) 492 472 value = endtag_re.sub(u'', value) 493 473 return value 494 removetags.is_safe = True495 474 496 475 @register.filter 497 @stringfilter 476 @stringfilter(is_safe=True) 498 477 def striptags(value): 499 478 """Strips all [X]HTML tags.""" 500 479 return strip_tags(value) 501 striptags.is_safe = True502 480 503 481 ################### 504 482 # LISTS #