Admin: ordering by multiple fields in a column sort

 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
from django.contrib import admin
from django.contrib.admin.views.main import ChangeList
from tunes.models import Song

class SpecialOrderingChangeList(ChangeList):
    def apply_special_ordering(self, queryset):
        order_type, order_by = [self.params.get(param, None) for param in ('ot', 'o')]
        special_ordering = self.model_admin.special_ordering
        if special_ordering and order_type and order_by:
            try:
                order_field = self.list_display[int(order_by)]
                ordering = special_ordering[order_field]
                if order_type == 'desc':
                    ordering = ['-' + field for field in ordering]
                queryset = queryset.order_by(*ordering)
            except IndexError:
                return queryset
            except KeyError:
                return queryset
        return queryset

    def get_query_set(self):
        queryset = super(SpecialOrderingChangeList, self).get_query_set()
        queryset = self.apply_special_ordering(queryset)
        return queryset

class SongAdmin(admin.ModelAdmin):
    list_display = ['name', 'time', 'artist', 'album', 'track', 'total_tracks']
    special_ordering = {'artist': ('artist', 'album', 'track'), 'album': ('album', 'track')}

    def get_changelist(self, request, **kwargs):
        return SpecialOrderingChangeList

admin.site.register(Song, SongAdmin)

More like this

  1. Yet another list partitioning filter by AndrewIngram 4 years ago
  2. Django admin inline ordering - javascript only implementation by ojhilt 5 months, 2 weeks ago
  3. iTunes Podcast RSS Feed by Kyle_Dickerson 2 years, 10 months ago
  4. Drag and drop ordering of admin list elements for Grappelli [v2] by brejoc 4 months, 3 weeks ago
  5. improved generic foreign key manager by carljm 4 years, 8 months ago

Comments

XelaRellum (on July 24, 2010):

Thanks a lot for that snippet. Was just about to get some bad headache about the multiple column sorting when I stumbled over this. Saved my day!!

Alex

#

gamesbook (on October 18, 2010):

This does not seem to work for extra fields defined in the model class using the "def Field" function... anyone able to upgrade it to do so?

#

xaralis (on December 2, 2010):

I have enhanced this a little by taking into account default ordering and also default model ordering if it's not defined in admin:

class SpecialOrderingChangeList(ChangeList):
    def apply_special_ordering(self, queryset):
        order_type, order_by = [self.params.get(param, None) for param in ('ot', 'o')]

        ordering_attr = self.model_admin.ordering or self.model._meta.ordering

        if order_type is None and order_by is None and ordering_attr is not None:
            order_type = 'desc' if ordering_attr[0] == '-' else 'asc'
            order_by = self.model_admin.list_display.index(ordering_attr[0])

        special_ordering = self.model_admin.special_ordering
        if special_ordering and order_type and order_by:
            try:
                order_field = self.list_display[int(order_by)]
                ordering = special_ordering[order_field]
                if order_type == 'desc':
                    ordering = ['-' + field for field in ordering]
                queryset = queryset.order_by(*ordering)
            except IndexError:
                return queryset
            except KeyError:
                return queryset
        return queryset

    def get_query_set(self):
        queryset = super(SpecialOrderingChangeList, self).get_query_set()
        queryset = self.apply_special_ordering(queryset)
        return queryset

#

gamesbook (on January 12, 2011):

The script by xaralis (on December 2, 2010) returns errors: list.index(x): x not in list

Any ideas on this?

#

xaralis (on February 28, 2011):

gamesbook: I've just bumped into this as well. You should double-check what you have in ordering and special_ordering attributes. The special_ordering should always contain attribute which is named as one of model's own, something special will not work, e.g.:

ordering = ('group__name',)
special_ordering = {'group_and_name': ('group__name', 'name')}

This won't work.

ordering = ('group',)
special_ordering = {'group': ('group__name', 'name')}

This will.

#

gautier (on July 26, 2011):

You saved my day! Thanks

Even though I quite changed it :

def SpecialOrderingChangeListFactory(*ordering):
    class SpecialOrderingChangeList(ChangeList):
        def get_query_set(self):
            queryset = super(SpecialOrderingChangeList, self).get_query_set()
            queryset = queryset.order_by(*ordering)
            return queryset

    return SpecialOrderingChangeList

class SubCategoryAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        return SpecialOrderingChangeListFactory("category__title", "title")

#

(Forgotten your password?)