diff -r b3394ccce096 django/contrib/admin/media/css/base.css
--- a/django/contrib/admin/media/css/base.css Tue May 31 21:29:35 2011 +0000
+++ b/django/contrib/admin/media/css/base.css Wed Jun 01 17:31:26 2011 +0100
@@ -326,6 +326,36 @@
background: url(../img/admin/arrow-down.gif) right .4em no-repeat;
}
+table thead th.sorted a span.text {
+ display: block;
+ float: left;
+}
+
+table thead th.sorted a span.sortpos {
+ display: block;
+ float: right;
+ font-size: .6em;
+}
+
+table thead th.sorted a img {
+ vertical-align: bottom;
+ /* Make it look like a link */
+ border-bottom: 1px solid #5b80b2;
+}
+
+table thead th.sorted a span.clear {
+ display: block;
+ clear: both;
+}
+
+#sorting-popup-div {
+ position: absolute;
+ background-color: white;
+ border: 1px solid #ddd;
+ z-index: 2000; /* more than filters on right */
+ padding-right: 10px;
+}
+
/* ORDERABLE TABLES */
table.orderable tbody tr td:hover {
diff -r b3394ccce096 django/contrib/admin/templates/admin/change_list_results.html
--- a/django/contrib/admin/templates/admin/change_list_results.html Tue May 31 21:29:35 2011 +0000
+++ b/django/contrib/admin/templates/admin/change_list_results.html Wed Jun 01 17:31:26 2011 +0100
@@ -1,3 +1,4 @@
+{% load adminmedia %}
{% if result_hidden_fields %}
{# DIV for HTML validation #}
{% for item in result_hidden_fields %}{{ item }}{% endfor %}
@@ -8,10 +9,18 @@
+
+{# Sorting popup: #}
+
+
+
{% endif %}
diff -r b3394ccce096 django/contrib/admin/templatetags/admin_list.py
--- a/django/contrib/admin/templatetags/admin_list.py Tue May 31 21:29:35 2011 +0000
+++ b/django/contrib/admin/templatetags/admin_list.py Wed Jun 01 17:31:26 2011 +0100
@@ -93,6 +93,7 @@
if field_name == 'action_checkbox':
yield {
"text": header,
+ "sort_pos": 0,
"class_attrib": mark_safe(' class="action-checkbox-column"')
}
continue
@@ -100,7 +101,7 @@
# It is a non-field, but perhaps one that is sortable
admin_order_field = getattr(attr, "admin_order_field", None)
if not admin_order_field:
- yield {"text": header}
+ yield {"text": header, "sort_pos": 0 }
continue
# So this _is_ a sortable non-field. Go to the yield
@@ -110,14 +111,44 @@
th_classes = []
new_order_type = 'asc'
- if field_name == cl.order_field or admin_order_field == cl.order_field:
- th_classes.append('sorted %sending' % cl.order_type.lower())
- new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
+ ordering_fields = cl.get_ordering_fields()
+ sort_pos = 0
+ if field_name in ordering_fields or admin_order_field in ordering_fields:
+ if not field_name in ordering_fields:
+ field_name = admin_order_field
+ order_type = ordering_fields.get(field_name).lower()
+ sort_pos = ordering_fields.keys().index(field_name) + 1
+ th_classes.append('sorted %sending' % order_type)
+ new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
+
+ # build new ordering param
+ o_list = []
+ make_qs_param = lambda t, n: ('-' if t == 'desc' else '') + str(n)
+
+ for f in ordering_fields.keys():
+ try:
+ n = cl.list_display.index(f)
+ except ValueError:
+ continue
+
+ if f == field_name:
+ # We want clicking on this header to bring the ordering to the
+ # front
+ o_list.insert(0, make_qs_param(new_order_type, n))
+ else:
+ o_list.append(make_qs_param(ordering_fields.get(f), n))
+
+ if field_name not in ordering_fields:
+ n = cl.list_display.index(field_name)
+ o_list.insert(0, make_qs_param(new_order_type, n))
+
+ o_list = '.'.join(o_list)
yield {
"text": header,
"sortable": True,
- "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
+ "sort_pos": sort_pos,
+ "url": cl.get_query_string({ORDER_VAR: o_list}),
"class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
}
@@ -231,6 +262,7 @@
return {'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': list(result_headers(cl)),
+ 'reset_order_url': cl.get_query_string(remove=[ORDER_VAR]),
'results': list(results(cl))}
@register.inclusion_tag('admin/date_hierarchy.html')
diff -r b3394ccce096 django/contrib/admin/views/main.py
--- a/django/contrib/admin/views/main.py Tue May 31 21:29:35 2011 +0000
+++ b/django/contrib/admin/views/main.py Wed Jun 01 17:31:26 2011 +0100
@@ -3,6 +3,7 @@
from django.core.exceptions import SuspiciousOperation
from django.core.paginator import InvalidPage
from django.db import models
+from django.utils.datastructures import SortedDict
from django.utils.encoding import force_unicode, smart_str
from django.utils.translation import ugettext, ugettext_lazy
from django.utils.http import urlencode
@@ -75,7 +76,7 @@
self.list_editable = ()
else:
self.list_editable = list_editable
- self.order_field, self.order_type = self.get_ordering()
+ self.ordering = self.get_ordering()
self.query = request.GET.get(SEARCH_VAR, '')
self.query_set = self.get_query_set(request)
self.get_results(request)
@@ -171,35 +172,46 @@
# manually-specified ordering from the query string.
ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
- if ordering[0].startswith('-'):
- order_field, order_type = ordering[0][1:], 'desc'
- else:
- order_field, order_type = ordering[0], 'asc'
if ORDER_VAR in params:
- try:
- field_name = self.list_display[int(params[ORDER_VAR])]
+ # Clear ordering and used params
+ ordering = []
+ order_params = params[ORDER_VAR].split('.')
+ for p in order_params:
try:
- f = lookup_opts.get_field(field_name)
- except models.FieldDoesNotExist:
- # See whether field_name is a name of a non-field
- # that allows sorting.
+ none, pfx, idx = p.rpartition('-')
+ field_name = self.list_display[int(idx)]
try:
- if callable(field_name):
- attr = field_name
- elif hasattr(self.model_admin, field_name):
- attr = getattr(self.model_admin, field_name)
- else:
- attr = getattr(self.model, field_name)
- order_field = attr.admin_order_field
- except AttributeError:
- pass
- else:
- order_field = f.name
- except (IndexError, ValueError):
- pass # Invalid ordering specified. Just use the default.
- if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
- order_type = params[ORDER_TYPE_VAR]
- return order_field, order_type
+ f = lookup_opts.get_field(field_name)
+ except models.FieldDoesNotExist:
+ # See whether field_name is a name of a non-field
+ # that allows sorting.
+ try:
+ if callable(field_name):
+ attr = field_name
+ elif hasattr(self.model_admin, field_name):
+ attr = getattr(self.model_admin, field_name)
+ else:
+ attr = getattr(self.model, field_name)
+ field_name = attr.admin_order_field
+ except AttributeError:
+ pass
+ else:
+ field_name = f.name
+
+ ordering.append(pfx + field_name)
+
+ except (IndexError, ValueError):
+ pass # Invalid ordering specified. Skip it.
+
+ return ordering
+
+ def get_ordering_fields(self):
+ # Returns a SortedDict of ordering fields and asc/desc
+ ordering_fields = SortedDict()
+ for o in self.ordering:
+ none, t, f = o.rpartition('-')
+ ordering_fields[f] = 'desc' if t == '-' else 'asc'
+ return ordering_fields
def get_lookup_params(self, use_distinct=False):
lookup_params = self.params.copy() # a dictionary of the query string
@@ -290,8 +302,8 @@
break
# Set ordering.
- if self.order_field:
- qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
+ if self.ordering:
+ qs = qs.order_by(*self.ordering)
# Apply keyword searches.
def construct_search(field_name):