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>