In many templates variables are passed to the query string [wiki] in a URL, for example:
<a href="?search={{ search }}&page={{ result.next_page_number }}"></a>
Why is it a problem?
The search
variable in this case might
contain special characters, for example one could search
for a string with a question mark (?
)
and/or ampersand (&
). If we work then
withfoo&bar=qux
as value for
{{ search }}
. If we directly print this in
the URL, then the URL now looks like
?search=foo&bar=qux&page=2
.
This is definitely not what is intended, and Django will
parse this as:
>>> QueryDict('search=foo&bar=qux&page=2')
<QueryDict: {'search': ['foo'], 'amp': [''], 'bar': ['qux'], 'page': ['2']}>
This thus means that we no longer look for
foo&bar
, but for foo
, and
it furthermore creates an extra number of items.
If search
contains a hash character
(#
), then this acts as the separator
between the query string and the
fragment of the URL. If we for example search
for foo#bar
, then we will retrieve
?query=foo#bar&page=2
, but the part
after the hash #
will be considered the
separator of the querystring with the fragment. For
example:
>>> urlparse('?search=foo#bar=qux&page=2')
ParseResult(scheme='', netloc='', path='', params='', query='search=foo', fragment='bar=qux&page=2')
so request.GET
will only have one item:
with as key search
and as value
foo
, the rest will not be interpreted by
the server.
What can be done to resolve the problem?
One can make use of the |urlencode
template filter [Django-doc] that will
percentage encode the values. This means that for
foo&bar=qux
, we get
?query=foo%26bar%3Dqux&page=2
, and for
foo#bar
we get
?query=foo%23bar&page=2
. These are then
converted to:
>>> QueryDict('query=foo%26bar%3Dqux&page=2')
<QueryDict: {'query': ['foo&bar=qux'], 'page': ['2']}>
>>> QueryDict('query=foo%23bar')
<QueryDict: {'query': ['foo#bar']}>
Django will thus interpret the boundary tokens between the items of the querystring, and the querystring with the fragment correctly and automatically percentage decode the items.
We thus should render this as:
<a href="?search={{ search|urlencode }}&page={{ result.next_page_number }}"></a>