How to sort a list of objects in a template in alphabetic order?
Asked Answered
M

4

11

I want to display a list of objects in a Django generic display view ListView class. And, to make it prettier, I try to sort it in alphabetic order. So, I use the built-in dictsort tag to sort the list.

Here is the summary of the code I used:

{% for item in object_list|dictsort:"name" %}
  ...
{% empty %}
  ...
{% endfor %}

The problem is that it sorts the names according to the ASCII values of the characters, meaning that bigcaps and smallcaps are sorted differently. Here is an example:

Bob
Eve
alice
zoe

And, what I would like to have is the following:

alice
Bob
Eve
zoe

I looked the documentation and several questions in SO, with no success. So, if someone has a way to achieve this, I would be extremely grateful.

Macbeth answered 2/2, 2014 at 14:9 Comment(0)
L
7

You'll need to write a custom filter which sorts by lower case. It's pretty simple:

@register.filter
def sort_lower(lst, key_name):
    return sorted(lst, key=lambda item: getattr(item, key_name).lower())

But if your list is a set of objects from the database, you shouldn't really be sorting them in Python - you should get the database to return them in the order you want.

Edit

How are you using the filter? It should be exactly the same as the dictsort one: object_list|sort_lower:"name".

To sort the queryset in the database, you can use the extra method to add a lower-case version of the field:

MyModel.objects.all().extra(select={'lower_name': 'LOWER(NAME)'}, order_by='lower_name')
Laciniate answered 2/2, 2014 at 14:28 Comment(5)
First, I tried to define my own filter as you mention it, but I didn't manage to make it work in the template. I have problem with argument passing within the for loop. And, second, how would you get the DB to return the sorted list from the template ?Macbeth
I get a 'MyClass' object has no attribute '__getitem__' error, I try to see why. But, I'm a bit stuck.Macbeth
Sorry, I've updated the code. But as I say, you should use the query version.Laciniate
I managed to get the first method to work in a similar way than dictsort. But, I don't see why adding an extra field to the database is better than sorting it after the request.Macbeth
this sounds very... English. What about when L10N is turned on?Gardant
H
6

A very old question, but the issue just came up for me. Just add .lower to dictsort, e.g.:

{% for item in object_list|dictsort:"name.lower" %}

Halbeib answered 18/9, 2020 at 14:7 Comment(0)
H
2

I know this is an old question, but the easiest way I see to sort a list case insensitive is:

name_of_list.sort(key=str.lower)

See link for reference: https://www.afternerd.com/blog/python-sort-list/

You can test it using this code:

my_list = ["alice", "Bob", "eve", "Zoe"]
my_list.sort()
print("case sensitive list:")
print(my_list)

my_list.sort(key=str.lower)
print("case insensitive list:")
print(my_list)

Then when you display your list in templates the items will already be sorted.

Hoover answered 27/6, 2019 at 20:42 Comment(0)
M
1

In fact, I looked at the original code of dictsort in .../lib/python2.7/site-packagesdjango/template/defaultfilters.py. And, I just added a customized cmp method:

@register.filter
def sort_lower(value, arg):
    try:
        return sorted(value, key=Variable(arg).resolve,
                      cmp=lambda x,y: cmp(x.lower(), y.lower()))
    except (TypeError, VariableDoesNotExist):
        return ''

This way allow to sort by subfields (e.g. field1.field2) which was needed in my case.

Macbeth answered 2/2, 2014 at 23:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.