Sort Table Headers

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
ORDER_VAR = 'o'
ORDER_TYPE_VAR = 'ot'

class SortHeaders:
    """
    Handles generation of an argument for the Django ORM's
    ``order_by`` method and generation of table headers which reflect
    the currently selected sort, based on defined table headers with
    matching sort criteria.

    Based in part on the Django Admin application's ``ChangeList``
    functionality.
    """
    def __init__(self, request, headers, default_order_field=None,
            default_order_type='asc', additional_params=None):
        """
        request
            The request currently being processed - the current sort
            order field and type are determined based on GET
            parameters.

        headers
            A list of two-tuples of header text and matching ordering
            criteria for use with the Django ORM's ``order_by``
            method. A criterion of ``None`` indicates that a header
            is not sortable.

        default_order_field
            The index of the header definition to be used for default
            ordering and when an invalid or non-sortable header is
            specified in GET parameters. If not specified, the index
            of the first sortable header will be used.

        default_order_type
            The default type of ordering used - must be one of
            ``'asc`` or ``'desc'``.

        additional_params:
            Query parameters which should always appear in sort links,
            specified as a dictionary mapping parameter names to
            values. For example, this might contain the current page
            number if you're sorting a paginated list of items.
        """
        if default_order_field is None:
            for i, (header, query_lookup) in enumerate(headers):
                if query_lookup is not None:
                    default_order_field = i
                    break
        if default_order_field is None:
            raise AttributeError('No default_order_field was specified and none of the header definitions given were sortable.')
        if default_order_type not in ('asc', 'desc'):
            raise AttributeError('If given, default_order_type must be one of \'asc\' or \'desc\'.')
        if additional_params is None: additional_params = {}

        self.header_defs = headers
        self.additional_params = additional_params
        self.order_field, self.order_type = default_order_field, default_order_type

        # Determine order field and order type for the current request
        params = dict(request.GET.items())
        if ORDER_VAR in params:
            try:
                new_order_field = int(params[ORDER_VAR])
                if headers[new_order_field][1] is not None:
                    self.order_field = new_order_field
            except (IndexError, ValueError):
                pass # Use the default
        if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'):
            self.order_type = params[ORDER_TYPE_VAR]

    def headers(self):
        """
        Generates dicts containing header and sort link details for
        all defined headers.
        """
        for i, (header, order_criterion) in enumerate(self.header_defs):
            th_classes = []
            new_order_type = 'asc'
            if i == self.order_field:
                th_classes.append('sorted %sending' % self.order_type)
                new_order_type = {'asc': 'desc', 'desc': 'asc'}[self.order_type]
            yield {
                'text': header,
                'sortable': order_criterion is not None,
                'url': self.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
                'class_attr': (th_classes and ' class="%s"' % ' '.join(th_classes) or ''),
            }

    def get_query_string(self, params):
        """
        Creates a query string from the given dictionary of
        parameters, including any additonal parameters which should
        always be present.
        """
        params.update(self.additional_params)
        return '?%s' % '&'.join(['%s=%s' % (param, value) \
                                     for param, value in params.items()])

    def get_order_by(self):
        """
        Creates an ordering criterion based on the current order
        field and order type, for use with the Django ORM's
        ``order_by`` method.
        """
        return '%s%s' % (
            self.order_type == 'desc' and '-' or '',
            self.header_defs[self.order_field][1],
        )

More like this

  1. Functional Filters by waterson 6 years, 6 months ago
  2. Yet another SQL debugging facility by miracle2k 6 years, 8 months ago
  3. Python Calendar wrapper template tag by dokterbob 4 years, 11 months ago
  4. NewForms Readonly / Edit Pattern by FreddieP 6 years, 4 months ago
  5. Group sequence into rows and columns for a TABLE by davidwtbuxton 3 years, 2 months ago

Comments

skam (on July 13, 2007):

line 66: except (IndexError, ValueError) instead of IndexError, ValueError

great work!

#

insin (on July 13, 2007):

Whoops, thanks for pointing that out - fixed it now.

#

sebastianmacias (on September 4, 2007):

Would it be possible to use ordering criteria that span relationships?

Lets say I have article model and each article belogs to a user.

I would like to order the artcles by user email

but I get an error when I add ('Email Address', 'user__email') to LIST_HEADERS.

Best,

Sebastian Macias

#

mirobe (on February 7, 2008):

I'm affraid there would be a problem when using pagination.

Do you have pagination enabled version of sorted headers list ?

Thanks

#

powerfox (on January 28, 2009):

Nice work! Thanks a lot.

#

blcArmadillo (on June 17, 2010):

Is there anyway to independently sort multiple tables on a single page?

#

darrinmc (on March 21, 2011):

I'm using this code with django 1.2.5 and I get the following error in a simple list view:

Exception Value: 'generator' object has no attribute 'GET'
Exception Location: /Users/darrinmc/Projects/django/djangotest/myapp/sort_headers.py in __init__, line 60

Line 60 of sort_headers.py is this line:

params = dict(request.GET.items())

In the debug output, the request object is a generator object when this method is called. Has anyone else seen this? Thanks.

#

darrinmc (on March 26, 2011):

I figured out my problem. It was unrelated to this code.

#

VeryCool (on May 3, 2011):

I had a search form in the list page with the method get. I had to add some extra lines to include the request get query string in the sorted link path. enjoy

[HTML_REMOVED]

#

VeryCool (on May 3, 2011):

if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'): self.order_type = params[ORDER_TYPE_VAR]

  • """
  • if there is a query string in the path include it in the sorted links.
  • """
  • if request.META.has_key('QUERY_STRING'):
  • query_string = request.META["QUERY_STRING"]
  • param_list = query_string.split('&')
  • for item in param_list :
  • k, v = item.split('=')
  • if k not in (ORDER_VAR,ORDER_TYPE_VAR) and v not in (ORDER_VAR,ORDER_TYPE_VAR):
  • additional_params.update({k:v})

#

(Forgotten your password?)