Django template filter - sort by multiple keys I have prepared an implementation of sort_by_keys filter:

{% for donation in donations|sort_by_keys:'date,-value' %}
    &lt;img src="{{ donation.stamp_img_url }}" /&gt;
{% endfor %}


Example data:

donations = [
    {
        date: '29-07-2014',
        value: 150,
    },
    {
        date: '07-10-2014',
        value: 450,
    },
    {
        date: '29-07-2014',
        value: 500,
    },
]


Expected result:

donations|sort_by_keys:'date,-value'


Should return:

[
    {
        date: '29-07-2014',
        value: 500,
    },
    {
        date: '29-07-2014',
        value: 150,
    },
    {
        date: '07-10-2014',
        value: 450,
    },
]


My implementation:

@register.filter
def sort_by_keys(value, keys):
    _list = list(value)

    def compare(item1, item2):
        for key in item1.__keys_for_sort.split(','):

            if key.startswith('-'):
                key = key.replace('-', '')

                if getattr(item1, key) &gt; getattr(item2, key):
                    return -1
                elif getattr(item1, key) &lt; getattr(item2, key):
                    return 1
            else:
                if getattr(item1, key) &lt; getattr(item2, key):
                    return -1
                elif getattr(item1, key) &gt; getattr(item2, key):
                    return 1

        return 0

    for item in _list:
        setattr(item, '__keys_for_sort', keys)

    _list = sorted(_list, cmp=compare)

    for item in _list:
        delattr(item, '__keys_for_sort')

    return _list


I really don't like the idea of injection special attributes like __keys_for_sort, to have possibility of using this value in comparator, but I don't know how to write this better.
keys is already visible in compare, so you can even do the splitting
outside of compare and still use it inside of it.  And the duplication
of code for the else branch should be eliminated, by only doing the
comparison once (cmp should work I guess) and then negating the
result if necessary.  This means processing all the keys before
running the sorting into a useful data-structure.  You want to compare
in the order they are specified, so a list of tuples will do fine.

Then, by using cmp instead of normal comparisons three lines per
branch are eliminated.  Also, which is maybe more important, you don't
have to worry about the comparisons and what -1/1, which personally
takes me way to long all the time while reading comparison functions
like this.

The two branches are also unnecessary, since you can just negate the
result depending on the sort key (factor in the following code).

So I arrive at this, untested of course.

@register.filter
def sort_by_keys(value, keys):
    transformed = []
    for key in keys.split(','):
        factor = 1
        if key.startswith('-'):
            key = key.replace('-', '')
            factor = -1
        transformed.append((key, factor))

    def compare(item1, item2):
        for (key, factor) in transformed:
            result = factor * cmp(getattr(item1, key), getattr(item2, key))
            if result != 0:
                return result
        return 0

    return sorted(list(value), cmp=compare)


It looks like you can't use sort directly on the input value?
Otherwise I'd suggest to do that as well.

Apart from completely compiling the sort function beforehand (using
lambdas), this seems the most compact I think.