Python multiple inheritance function overriding and ListView in django

Jorge Barata picture Jorge Barata · Mar 30, 2012 · Viewed 11k times · Source

I created a class that subclasses ListView and two custom mixins which have implemented a get_context_data function. I wanted to override this function on the child class:

from django.views.generic import ListView

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return kwargs

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return kwargs

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
  def get_context_data(self, **context):
    super(ListSortedMixin,self).get_context_data(**context)
    super(ListPaginatedMixin,self).get_context_data(**context)
    return context

When I execute MyListView it only prints "ListSortedMixin". For some reason python is executing ListSortedMixin.get_context_data in place of MyListView.get_context_data. Why?

If I change the inheritance order to ListPaginatedMixin, ListSortedMixin, ListView, ListPaginatedMixin.get_context_data is executed.

How can I override the get_context_data function?

Answer

Daniel Palm picture Daniel Palm · Jul 21, 2013

This is an old question, but I believe the answer is incorrect. There is a mistake in your code. It should read:

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
    def get_context_data(self, **context):
        super(MyListView,self).get_context_data(**context)
        return context

The order in which the get_context_data will be called follows the same order as specified in the declaration of MyListView. Notice the argument of super is MyListView and not the super classes.

UPDATE:

I missed that your mixins don't call super. They should. Yes, even if they inherit from object, because super calls the next method in the MRO, not necessarily the parent of class it is in.

from django.views.generic import ListView

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return super(ListSortedMixin,self).get_context_data(**context)

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return super(ListPaginatedMixin,self).get_context_data(**context)

class MyListView(ListSortedMixin, ListPaginatedMixin, ListView):
    def get_context_data(self, **context):
        return super(MyListView,self).get_context_data(**context)

For MyListView the MRO is then:

  1. MyListView
  2. ListSortedMixin
  3. ListPaginatedMixin
  4. ListView
  5. Whatever is above ListView ... n. object

Calling them one by one may work, but is not how it was intended to be used.

UPDATE 2

Copy and paste example to prove my point.

class Parent(object):
    def get_context_data(self, **kwargs):
        print 'Parent'

class ListSortedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListSortedMixin'
        return super(ListSortedMixin,self).get_context_data(**kwargs)

class ListPaginatedMixin(object):
    def get_context_data(self, **kwargs):
        print 'ListPaginatedMixin'
        return super(ListPaginatedMixin,self).get_context_data(**kwargs)

class MyListView(ListSortedMixin, ListPaginatedMixin, Parent):
    def get_context_data(self, **kwargs):
        return super(MyListView,self).get_context_data(**kwargs)


m = MyListView()
m.get_context_data(l='l')