Blog Archive

Monday, 16 January 2012

Django - Adding row to the admin list with the totals and averages of given fields

Once a while a wild ticked occurred for one of the projects I was developing.
The issue was that they needed a rows with the totals and averages to be displayed at the end of the table in the admin changelist view for particular fields.
So here how it works

from django.contrib.admin.views.main import ChangeList
from django.db.models import Sum, Avg
from myapp.models import MyModelName
class TotalAveragesChangeList(ChangeList):
    #provide the list of fields that we need to calculate averages and totals
    fields_to_total = ['amount', 'total_sum', 'paid_by_cash',
    def get_total_values(self, queryset):
        Get the totals
        #basically the total parameter is an empty instance of the given model
        total =  MyModelName()
        total.custom_alias_name = "Totals" #the label for the totals row
        for field in self.fields_to_total:
            setattr(total, field, queryset.aggregate(Sum(field)).items()[0][1])
        return total
    def get_average_values(self, queryset):
        Get the averages
        average =  MyModelName()
        average.custom_alias_name = "Averages" #the label for the averages row
        for field in self.fields_to_total:
            setattr(average, field, queryset.aggregate(Avg(field)).items()[0][1])
        return average
    def get_results(self, request):
        The model admin gets queryset results from this method
        and then displays it in the template
        super(TotalAveragesChangeList, self).get_results(request)
        #first get the totals from the current changelist
        total = self.get_total_values(self.query_set)
        #then get the averages
        average = self.get_average_values(self.query_set)
        #small hack. in order to get the objects loaded we need to call for 
        #queryset results once so simple len function does it
        #and finally we add our custom rows to the resulting changelist

And all we need is just to set our custom changelist to the AdminModel

class MyModelNameAdmin(admin.ModelAdmin):
    def get_changelist(self, request, **kwargs):
        return TotalAveragesChangeList, MyModelNameAdmin)


  1. Thanks a ton! I was looking for a way to figure this out

  2. Thanks AzMan, very usefull code. Could you post the code for?:
    - where you use custom_alias_name as label.
    - where you set the CSS or clases for row background colors

    Other question: The file is the correct place to define the TotalAveragesChangeList class?

    Thanks a lot

    1. You can place it there or take out into any outside module. I would place it into a different file, out of admin.
      I have overriden built in admin css file using static files.

  3. Hi Azamat,

    May I ask question?
    Where does total.custom_alias_name = "Totals" #the label for the totals row come from?

    I am getting the value correct but without label in the row.

    Thnaks for your blog


    1. Same question... the answer dont work for me...

  4. You need to add custom_alias_name property in your model.

    For example like this

    def __unicode__(self):
    if self.title:
    return self.title
    return self.custom_alias_name

    It has to be defined in your model class
    So what happens here is that when it is an instance of model that is not loaded from db it will return the custom_alias_name instead of title

    1. Cheers Azamat,
      You have given me hint,

      Keep on writing more articles.


  5. In my case the subtotal is a calculated field (read_only). How to use in your method on the fields_to_total?
    class DetVenda(models.Model):
    quantity = models.IntegerField()
    price = models.DecimalField(max_digits=8, decimal_places=2)

    def _get_subtotal(self):
    if self.quantity:
    return self.price * self.quantity
    subtotal = property(_get_subtotal)
    class TotalChangeList(ChangeList):
    fields_to_total = ['subtotal']

    def get_total_values(self, queryset):
    total = Sale()
    total.custom_alias_name = "Totals"
    for field in self.fields_to_total:
    total, field, queryset.aggregate(Sum(field)).items()[0][1])
    return total

    def get_results(self, request):
    super(TotalChangeList, self).get_results(request)
    total = self.get_total_values(self.query_set)

    FieldError at /admin/Sale:

    Cannot resolve keyword 'total' into field. Choices are:

    How to resolve this?

  6. hello
    I'm very new to django.
    What is self.query_set in your example