Django — Override ModelAdmin ChangeList Ordering By Annotate and Aggregate

I ran into trouble overriding django’s ModelAdmin ChangeList Ordering.

Overriding the ModelAdmin.queryset did not help because while the Queryset can be defined, the ordering is overridden by `ChangeList.get_ordering`

My solution is to override the ModelAdmin get_changelist method and return a subclassed ChangeList.

class AccountAdmin(admin.ModelAdmin):   
    # ...
    
    def get_changelist(self, request, **kwargs):
        """
        Returns the ChangeList class for use on the changelist page.
        
        Force get_ordering to return None (if no ordering specified)
        to prevent  from applying ordering.
        """
        from django.contrib.admin.views.main import ChangeList
        
        class SortedChangeList(ChangeList):
            def get_query_set(self, *args, **kwargs):
                qs = super(SortedChangeList, self).get_query_set(*args, **kwargs)
                return qs.annotate(amt=Sum('entry__amount')).order_by('-amt')
                
        if request.GET.get('o'):
            return ChangeList
            
        return SortedChangeList

We take advantage of the fact that get_changelist can be overridden, and the fact that get_query_set is called AFTER ordering is done on the ChangeList.

Let me know if you have other solutions!

8 Comments

  1. Vitaliy says:

    Thank you very much! You save me a lot of time. You rock star!!!

  2. Allan Boll says:

    Good stuff. Thanks!
    Why the request.GET.get(‘o’):

    1. Yuji says:

      So as to not break default admin ordering. Otherwise the only ordering we could apply is our custom one.

      Also, Django 1.4 has multi column ordering and the syntax has changed so we’ll have to account for that in the future…

  3. Allan Boll says:

    ah, cool, thanks. Yes, I saw in the docs for 1.4 that’t it can be done a little more elegantly there – but that’ll have to wait till 1.4 is the recommended stable version.

  4. Derek says:

    Have you tried this code with Django 1.4 or 1.5?

  5. Austin says:

    Derek, I have just implemented with 1.4
    I’m using it a bit differently as I’m overriding the search query to allow users to not have to enter in dashes when searching for phone number

    def get_changelist(self, request, **kwargs):
    # Allow users to not have to enter in ‘-‘ when searching by phone #
    from django.contrib.admin.views.main import ChangeList

    if request.GET.get(‘q’):
    class NewChangeList(ChangeList):
    def get_query_set(self, *args, **kwargs):
    query = self.query
    if query.isdigit() and len(query) == 10:
    self.query = ‘-‘.join(
    (query[:3], query[3:6], query[6:])
    )
    return super(NewChangeList, self).get_query_set(
    *args, **kwargs
    )
    return NewChangeList
    else:
    return ChangeList

  6. gamesb00k says:

    OK; that is interesting. I have been struggling to get a search working on date or datetime fields…

  7. gamesb00k says:

    Maybe you can add that new code to your original post as an update; the formatting gets lost in the comments…

Leave a reply to Austin Cancel reply