I used the approach followed by the most voted answer above. But I solve the ascending/descending ordering when clicking multiple times on the column header by using a custom tag.
The tag:
from urllib.parse import urlencode
from collections import OrderedDict
@register.simple_tag
def url_replace(request, field, value, direction=''):
dict_ = request.GET.copy()
if field == 'order_by' and field in dict_.keys():
if dict_[field].startswith('-') and dict_[field].lstrip('-') == value:
dict_[field] = value
elif dict_[field].lstrip('-') == value:
dict_[field] = "-" + value
else:
dict_[field] = direction + value
else:
dict_[field] = direction + value
return urlencode(OrderedDict(sorted(dict_.items())))
Then you use this tag on your column header, like above:
<th><a href="?{% url_replace request 'order_by' 'name' '-' %}">Name</a></th>
First time you click it will sort in 'descending' order, if you click the same header again it will revert to 'ascending' order.
This approach also preserves other parameters in your URL, like page number if you are using a paginator. It requires no extra libraries. Only thing you need to make sure is that your view is sending the RequestContext to the template.