Ticket #11868: 11868_luke1.diff

File 11868_luke1.diff, 11.7 KB (added by lukeplant, 4 years ago)

Part way implementation of latest proposal

  • django/contrib/admin/media/css/base.css

    diff -r b3394ccce096 django/contrib/admin/media/css/base.css
    a b  
    326326    background: url(../img/admin/arrow-down.gif) right .4em no-repeat;
    327327}
    328328
     329table thead th.sorted a span.text {
     330   display: block;
     331   float: left;
     332}
     333
     334table thead th.sorted a span.sortpos {
     335   display: block;
     336   float: right;
     337   font-size: .6em;
     338}
     339
     340table thead th.sorted a img {
     341   vertical-align: bottom;
     342   /* Make it look like a link */
     343   border-bottom: 1px solid #5b80b2;
     344}
     345
     346table thead th.sorted a span.clear {
     347   display: block;
     348   clear: both;
     349}
     350
     351#sorting-popup-div {
     352    position: absolute;
     353    background-color: white;
     354    border: 1px solid #ddd;
     355    z-index: 2000; /* more than filters on right */
     356    padding-right: 10px;
     357}
     358
    329359/* ORDERABLE TABLES */
    330360
    331361table.orderable tbody tr td:hover {
  • django/contrib/admin/templates/admin/change_list_results.html

    diff -r b3394ccce096 django/contrib/admin/templates/admin/change_list_results.html
    a b  
     1{% load adminmedia %}
    12{% if result_hidden_fields %}
    23<div class="hiddenfields">{# DIV for HTML validation #}
    34{% for item in result_hidden_fields %}{{ item }}{% endfor %}
     
    89<table id="result_list">
    910<thead>
    1011<tr>
    11 {% for header in result_headers %}<th scope="col"{{ header.class_attrib }}>
    12 {% if header.sortable %}<a href="{{ header.url }}">{% endif %}
    13 {{ header.text|capfirst }}
    14 {% if header.sortable %}</a>{% endif %}</th>{% endfor %}
     12{% for header in result_headers %}
     13<th scope="col" {{ header.class_attrib }}>
     14  {% if header.sortable %}<a href="{{ header.url }}">{% endif %}
     15  <span class="text">{{ header.text|capfirst }}</span>
     16  {% if header.sortable %}
     17    {% if header.sort_pos > 0 %}<span class="sortpos">
     18      {% if header.sort_pos == 1 %}<img id="primary-sort-icon" src="{% admin_media_prefix %}img/admin/icon_primary_sort.png" alt="Primary sort field" />{% endif %}
     19      {{ header.sort_pos }}</span>
     20    {% endif %}
     21    <span class="clear"></span></a>
     22  {% endif %}
     23</th>{% endfor %}
    1524</tr>
    1625</thead>
    1726<tbody>
     
    2433</tbody>
    2534</table>
    2635</div>
     36
     37{# Sorting popup: #}
     38<div style="display: none;" id="sorting-popup-div">
     39<p>Sorting by:</p>
     40<ol>
     41{% for header in result_headers|dictsort:"sort_pos" %}
     42  {% if header.sort_pos > 0 %}
     43    <li>{{ header.text|capfirst }}</li>
     44  {% endif %}
     45{% endfor %}
     46</ol>
     47<p><a href="{{ reset_order_url }}">Reset sorting</a></p>
     48</div>
     49<script type="text/javascript">
     50<!--
     51(function($) {
     52    $(document).ready(function() {
     53        var popup = $('#sorting-popup-div');
     54        /* These next lines seems necessary to prime the popup: */
     55        popup.offset({left:-1000, top:0});
     56        popup.show();
     57        var popupWidth = popup.width()
     58        popup.hide();
     59
     60        $('#primary-sort-icon').toggle(function(ev) {
     61                                          ev.preventDefault();
     62                                          var img = $(this);
     63                                          var pos = img.offset();
     64                                          pos.top += img.height();
     65                                          if (pos.left + popupWidth >
     66                                              $(window).width()) {
     67                                              pos.left -= popupWidth;
     68                                          }
     69                                          popup.show();
     70                                          popup.offset(pos);
     71                                      },
     72                                      function(ev) {
     73                                          ev.preventDefault();
     74                                          popup.hide();
     75                                      });
     76    });
     77})(django.jQuery);
     78//-->
     79</script>
     80
    2781{% endif %}
  • django/contrib/admin/templatetags/admin_list.py

    diff -r b3394ccce096 django/contrib/admin/templatetags/admin_list.py
    a b  
    9393            if field_name == 'action_checkbox':
    9494                yield {
    9595                    "text": header,
     96                    "sort_pos": 0,
    9697                    "class_attrib": mark_safe(' class="action-checkbox-column"')
    9798                }
    9899                continue
     
    100101            # It is a non-field, but perhaps one that is sortable
    101102            admin_order_field = getattr(attr, "admin_order_field", None)
    102103            if not admin_order_field:
    103                 yield {"text": header}
     104                yield {"text": header, "sort_pos": 0 }
    104105                continue
    105106
    106107            # So this _is_ a sortable non-field.  Go to the yield
     
    110111
    111112        th_classes = []
    112113        new_order_type = 'asc'
    113         if field_name == cl.order_field or admin_order_field == cl.order_field:
    114             th_classes.append('sorted %sending' % cl.order_type.lower())
    115             new_order_type = {'asc': 'desc', 'desc': 'asc'}[cl.order_type.lower()]
     114        ordering_fields = cl.get_ordering_fields()
     115        sort_pos = 0
     116        if field_name in ordering_fields or admin_order_field in ordering_fields:
     117            if not field_name in ordering_fields:
     118                field_name = admin_order_field
     119            order_type = ordering_fields.get(field_name).lower()
     120            sort_pos = ordering_fields.keys().index(field_name) + 1
     121            th_classes.append('sorted %sending' % order_type)
     122            new_order_type = {'asc': 'desc', 'desc': 'asc'}[order_type]
     123
     124        # build new ordering param
     125        o_list = []
     126        make_qs_param = lambda t, n: ('-' if t == 'desc' else '') + str(n)
     127
     128        for f in ordering_fields.keys():
     129            try:
     130                n = cl.list_display.index(f)
     131            except ValueError:
     132                continue
     133
     134            if f == field_name:
     135                # We want clicking on this header to bring the ordering to the
     136                # front
     137                o_list.insert(0, make_qs_param(new_order_type, n))
     138            else:
     139                o_list.append(make_qs_param(ordering_fields.get(f), n))
     140
     141        if field_name not in ordering_fields:
     142            n = cl.list_display.index(field_name)
     143            o_list.insert(0, make_qs_param(new_order_type, n))
     144
     145        o_list = '.'.join(o_list)
    116146
    117147        yield {
    118148            "text": header,
    119149            "sortable": True,
    120             "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
     150            "sort_pos": sort_pos,
     151            "url": cl.get_query_string({ORDER_VAR: o_list}),
    121152            "class_attrib": mark_safe(th_classes and ' class="%s"' % ' '.join(th_classes) or '')
    122153        }
    123154
     
    231262    return {'cl': cl,
    232263            'result_hidden_fields': list(result_hidden_fields(cl)),
    233264            'result_headers': list(result_headers(cl)),
     265            'reset_order_url': cl.get_query_string(remove=[ORDER_VAR]),
    234266            'results': list(results(cl))}
    235267
    236268@register.inclusion_tag('admin/date_hierarchy.html')
  • django/contrib/admin/views/main.py

    diff -r b3394ccce096 django/contrib/admin/views/main.py
    a b  
    33from django.core.exceptions import SuspiciousOperation
    44from django.core.paginator import InvalidPage
    55from django.db import models
     6from django.utils.datastructures import SortedDict
    67from django.utils.encoding import force_unicode, smart_str
    78from django.utils.translation import ugettext, ugettext_lazy
    89from django.utils.http import urlencode
     
    7576            self.list_editable = ()
    7677        else:
    7778            self.list_editable = list_editable
    78         self.order_field, self.order_type = self.get_ordering()
     79        self.ordering = self.get_ordering()
    7980        self.query = request.GET.get(SEARCH_VAR, '')
    8081        self.query_set = self.get_query_set(request)
    8182        self.get_results(request)
     
    171172        # manually-specified ordering from the query string.
    172173        ordering = self.model_admin.ordering or lookup_opts.ordering or ['-' + lookup_opts.pk.name]
    173174
    174         if ordering[0].startswith('-'):
    175             order_field, order_type = ordering[0][1:], 'desc'
    176         else:
    177             order_field, order_type = ordering[0], 'asc'
    178175        if ORDER_VAR in params:
    179             try:
    180                 field_name = self.list_display[int(params[ORDER_VAR])]
     176            # Clear ordering and used params
     177            ordering = []
     178            order_params = params[ORDER_VAR].split('.')
     179            for p in order_params:
    181180                try:
    182                     f = lookup_opts.get_field(field_name)
    183                 except models.FieldDoesNotExist:
    184                     # See whether field_name is a name of a non-field
    185                     # that allows sorting.
     181                    none, pfx, idx = p.rpartition('-')
     182                    field_name = self.list_display[int(idx)]
    186183                    try:
    187                         if callable(field_name):
    188                             attr = field_name
    189                         elif hasattr(self.model_admin, field_name):
    190                             attr = getattr(self.model_admin, field_name)
    191                         else:
    192                             attr = getattr(self.model, field_name)
    193                         order_field = attr.admin_order_field
    194                     except AttributeError:
    195                         pass
    196                 else:
    197                     order_field = f.name
    198             except (IndexError, ValueError):
    199                 pass # Invalid ordering specified. Just use the default.
    200         if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
    201             order_type = params[ORDER_TYPE_VAR]
    202         return order_field, order_type
     184                        f = lookup_opts.get_field(field_name)
     185                    except models.FieldDoesNotExist:
     186                        # See whether field_name is a name of a non-field
     187                        # that allows sorting.
     188                        try:
     189                            if callable(field_name):
     190                                attr = field_name
     191                            elif hasattr(self.model_admin, field_name):
     192                                attr = getattr(self.model_admin, field_name)
     193                            else:
     194                                attr = getattr(self.model, field_name)
     195                            field_name = attr.admin_order_field
     196                        except AttributeError:
     197                            pass
     198                    else:
     199                        field_name = f.name
     200
     201                    ordering.append(pfx + field_name)
     202
     203                except (IndexError, ValueError):
     204                    pass # Invalid ordering specified. Skip it.
     205
     206        return ordering
     207
     208    def get_ordering_fields(self):
     209        # Returns a SortedDict of ordering fields and asc/desc
     210        ordering_fields = SortedDict()
     211        for o in self.ordering:
     212            none, t, f = o.rpartition('-')
     213            ordering_fields[f] = 'desc' if t == '-' else 'asc'
     214        return ordering_fields
    203215
    204216    def get_lookup_params(self, use_distinct=False):
    205217        lookup_params = self.params.copy() # a dictionary of the query string
     
    290302                            break
    291303
    292304        # Set ordering.
    293         if self.order_field:
    294             qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
     305        if self.ordering:
     306            qs = qs.order_by(*self.ordering)
    295307
    296308        # Apply keyword searches.
    297309        def construct_search(field_name):
Back to Top