Rewrote the section about writing autoescaping-aware filters, based on feedback
from Ivan Sagalaev. git-svn-id: http://code.djangoproject.com/svn/django/trunk@6692 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
38d972b9ec
commit
86ca11dd6d
|
@ -755,61 +755,106 @@ inside the template code:
|
||||||
``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry
|
``EscapeString`` and ``EscapeUnicode``. You will not normally need to worry
|
||||||
about these; they exist for the implementation of the ``escape`` filter.
|
about these; they exist for the implementation of the ``escape`` filter.
|
||||||
|
|
||||||
Inside your filter, you will need to think about three areas in order to be
|
When you are writing a filter, your code will typically fall into one of two
|
||||||
auto-escaping compliant:
|
situations:
|
||||||
|
|
||||||
1. If your filter returns a string that is ready for direct output (it should
|
1. Your filter does not introduce any HTML-unsafe characters (``<``, ``>``,
|
||||||
be considered a "safe" string), you should call
|
``'``, ``"`` or ``&``) into the result that were not already present. In
|
||||||
``django.utils.safestring.mark_safe()`` on the result prior to returning.
|
this case, you can let Django take care of all the auto-escaping handling
|
||||||
This will turn the result into the appropriate ``SafeData`` type. This is
|
for you. All you need to do is put the ``is_safe`` attribute on your
|
||||||
often the case when you are returning raw HTML, for example.
|
filter function and set it to ``True``. This attribute tells Django that
|
||||||
|
is a "safe" string is passed into your filter, the result will still be
|
||||||
|
"safe" and if a non-safe string is passed in, Django will automatically
|
||||||
|
escape it, if necessary. The reason ``is_safe`` is necessary is because
|
||||||
|
there are plenty of normal string operations that will turn a ``SafeData``
|
||||||
|
object back into a normal ``str`` or ``unicode`` object and, rather than
|
||||||
|
try to catch them all, which would be very difficult, Django repairs the
|
||||||
|
damage after the filter has completed.
|
||||||
|
|
||||||
2. If your filter is given a "safe" string, is it guaranteed to return a
|
For example, suppose you have a filter that adds the string ``xx`` to the
|
||||||
"safe" string? If so, set the ``is_safe`` attribute on the function to be
|
end of any input. Since this introduces no dangerous HTML characters into
|
||||||
``True``. For example, a filter that replaced a word consisting only of
|
the result (aside from any that were already present), you should mark
|
||||||
digits with the number spelt out in words is going to be
|
your filter with ``is_safe``::
|
||||||
safe-string-preserving, since it cannot introduce any of the five dangerous
|
|
||||||
characters: <, >, ", ' or &. We can write::
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def convert_to_words(value):
|
def add_xx(value):
|
||||||
# ... implementation here ...
|
return '%sxx' % value
|
||||||
return result
|
add_xx.is_safe = True
|
||||||
|
|
||||||
convert_to_words.is_safe = True
|
When this filter is used in a template where auto-escaping is enabled,
|
||||||
|
Django will escape the output whenever the input is not already marked as
|
||||||
|
"safe".
|
||||||
|
|
||||||
Note that this filter does not return a universally safe result (it does
|
By default, ``is_safe`` defaults to ``False`` and you can omit it from
|
||||||
not return ``mark_safe(result)``) because if it is handed a raw string such
|
any filters where it isn't required.
|
||||||
as '<a>', this will need further escaping in an auto-escape environment.
|
|
||||||
The ``is_safe`` attribute only talks about the the result when a safe
|
|
||||||
string is passed into the filter.
|
|
||||||
|
|
||||||
3. Will your filter behave differently depending upon whether auto-escaping
|
Be careful when deciding if your filter really does leave safe strings
|
||||||
is currently in effect or not? This is normally a concern when you are
|
as safe. Sometimes if you are *removing* characters, you can
|
||||||
returning mixed content (HTML elements mixed with user-supplied content).
|
inadvertently leave unbalanced HTML tags or entities in the result.
|
||||||
For example, the ``ordered_list`` filter that ships with Django needs to
|
For example, removing a ``>`` from the input might turn ``<a>`` into
|
||||||
know whether to escape its content or not. It will always return a safe
|
``<a``, which would need to be escaped on output to avoid causing
|
||||||
string. Since it returns raw HTML, we cannot apply escaping to the
|
problems. Similarly, removing a semicolon (``;``) can turn ``&``
|
||||||
result -- it needs to be done in-situ.
|
into ``&``, which is no longer a valid entity and thus needs
|
||||||
|
further escaping. Most cases won't be nearly this tricky, but keep an
|
||||||
|
eye out for any problems like that when reviewing your code.
|
||||||
|
|
||||||
For these cases, the filter function needs to be told what the current
|
2. Alternatively, your filter code can manually take care of any necessary
|
||||||
auto-escaping setting is. Set the ``needs_autoescape`` attribute on the
|
escaping. This is usually necessary when you are introducing new HTML
|
||||||
filter to ``True`` and have your function take an extra argument called
|
markup into the result. You want to mark the output as safe from further
|
||||||
``autoescape`` with a default value of ``None``. When the filter is called,
|
escaping so that your HTML markup isn't escaped further, so you'll need to
|
||||||
the ``autoescape`` keyword argument will be ``True`` if auto-escaping is in
|
handle the input yourself.
|
||||||
effect. For example, the ``unordered_list`` filter is written as::
|
|
||||||
|
|
||||||
def unordered_list(value, autoescape=None):
|
To mark the output as a safe string, use
|
||||||
# ... lots of code here ...
|
``django.utils.safestring.mark_safe()``.
|
||||||
|
|
||||||
return mark_safe(...)
|
Be careful, though. You need to do more than just mark the output as
|
||||||
|
safe. You need to ensure it really *is* safe and what you do will often
|
||||||
|
depend upon whether or not auto-escaping is in effect. The idea is to
|
||||||
|
write filters than can operate in templates where auto-escaping is either
|
||||||
|
on or off in order to make things easier for your template authors.
|
||||||
|
|
||||||
unordered_list.is_safe = True
|
In order for you filter to know the current auto-escaping state, set the
|
||||||
unordered_list.needs_autoescape = True
|
``needs_autoescape`` attribute to ``True`` on your function (if you don't
|
||||||
|
specify this attribute, it defaults to ``False``). This attribute tells
|
||||||
|
Django that your filter function wants to be passed an extra keyword
|
||||||
|
argument, called ``autoescape`` that is ``True`` is auto-escaping is in
|
||||||
|
effect and ``False`` otherwise.
|
||||||
|
|
||||||
By default, both the ``is_safe`` and ``needs_autoescape`` attributes are
|
An example might make this clearer. Let's write a filter that emphasizes
|
||||||
``False``. You do not need to specify them if ``False`` is an acceptable
|
the first character of a string::
|
||||||
value.
|
|
||||||
|
from django.utils.html import conditional_escape
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
def initial_letter_filter(text, autoescape=None):
|
||||||
|
first, other = text[0] ,text[1:]
|
||||||
|
if autoescape:
|
||||||
|
esc = conditional_escape
|
||||||
|
else:
|
||||||
|
esc = lambda x: x
|
||||||
|
result = '<strong>%s</strong>%s' % (esc(first), esc(other))
|
||||||
|
return mark_safe(result)
|
||||||
|
initial_letter_filter.needs_autoescape = True
|
||||||
|
|
||||||
|
The ``needs_autoescape`` attribute on the filter function and the
|
||||||
|
``autoescape`` keyword argument mean that our function will know whether
|
||||||
|
or not automatic escaping is in effect when the filter is called. We use
|
||||||
|
``autoescape`` to decide whether the input data needs to be passed through
|
||||||
|
``django.utils.html.conditional_escape`` or not (in the latter case, we
|
||||||
|
just use the identity function as the "escape" function). The
|
||||||
|
``conditional_escape()`` function is like ``escape()`` except it only
|
||||||
|
escapes input that is **not** a ``SafeData`` instance. If a ``SafeData``
|
||||||
|
instance is passed to ``conditional_escape()``, the data is returned
|
||||||
|
unchanged.
|
||||||
|
|
||||||
|
Finally, in the above example, we remember to mark the result as safe
|
||||||
|
so that our HTML is inserted directly into the template without further
|
||||||
|
escaping.
|
||||||
|
|
||||||
|
There is no need to worry about the ``is_safe`` attribute in this case
|
||||||
|
(although including it wouldn't hurt anything). Whenever you are manually
|
||||||
|
handling the auto-escaping issues and returning a safe string, the
|
||||||
|
``is_safe`` attribute won't change anything either way.
|
||||||
|
|
||||||
Writing custom template tags
|
Writing custom template tags
|
||||||
----------------------------
|
----------------------------
|
||||||
|
@ -932,7 +977,9 @@ without having to be parsed multiple times.
|
||||||
Auto-escaping considerations
|
Auto-escaping considerations
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The output from template tags is not automatically run through the
|
**New in Django development version**
|
||||||
|
|
||||||
|
The output from template tags is **not** automatically run through the
|
||||||
auto-escaping filters. However, there are still a couple of things you should
|
auto-escaping filters. However, there are still a couple of things you should
|
||||||
keep in mind when writing a template tag:
|
keep in mind when writing a template tag:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue