Arlan Kaliyev
Arlan Kaliyev

Reputation: 21

Why do we call super().get_queryset() in Django's get_queryset method?

I have a method in my Django project:

def get_queryset(self):
    queryset = super(OrderListView, self).get_queryset()
    return queryset.filter(initiator=self.request.user)

I don’t understand why we use queryset = super(OrderListView, self).get_queryset() inside get_queryset().

What is the purpose of this line? What exactly does it do? Why do we call super() here? Why do we call the same method (get_queryset) inside itself? I've tried to understand it, but explanations so far haven't made it clear to me. I would appreciate a simple and satisfying answer.

Upvotes: -1

Views: 56

Answers (3)

I think you need to understand the fundamentals of object oriented programming and the concept of overriding.

Basically when you inherit from a Parent/Base Class, you are inheriting all the properties and methods of the Class which are now available in your Child Class.

There might be cases where you need to override a method but you still want to keep parts of the implementations of the method from the parent class. Remember DRY - Do Not Repeat Yourself, In this case you do not have to reinvent the wheel or implement those functionalities all over again, you can simply call super().method_name() to make those implementations available in your child class method and modify it. if not, the features of the method from the parent class will not be available.

I will give you an example below. Considering the following classes of Shape and Square.

The Square class inherits from Shape and overrides the area and describe methods to provide a more specific implementation for a square. However, it still calls super().area() and super().describe() to reuse the parent class's logic and then adds additional behaviour.

class Shape:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        # Default implementation for a generic shape
        return self.length * self.width

    def describe(self):
        return f"This is a shape with side length {self.length} and side width {self.width}."


class Square(Shape):
    def __init__(self, side_length):
        super().__init__(side_length, side_length)  # Initialize the parent class

    def area(self):
        # Call the parent class's area() method
        parent_area = super().area()
        print(f"Parent class area: {parent_area}")

        # Add specific logic for a square
        square_area = self.side_length ** 2
        print(f"Square area: {square_area}")
        return square_area

    def describe(self):
        # Extend the parent class's describe() method
        parent_description = super().describe()
        return f"{parent_description} Specifically, this is a square."

*** TESTING ***


# Create a Square object
square = Square(5)  



# Call the overridden area() method
print("Area of the square:", square.area()) 

# outputs => Parent class area: 25

# outputs => Area of the square: 25

# Call the overridden describe() method
print(square.describe())  

# outputs => This is a shape with side length 5 and side width 5. Specifically, this is a square.

As you can see in the output of the area for the square object, we have overridden the area and describe methods but we call super().area() and super().describe() to make the properties of the methods from the parent available and add modification to them.

Now in your case in Django where you have to call queryset = super(OrderListView, self).get_queryset().

It is responsible for calling the parent get_queryset() which has some implementations, one of which is responsible for fetching the objects from the database. This is of course abstracted.

Apparently what you are doing is filtering the results of the fetched objects and alternatively, you could decide to write a logic to fetch the objects from scratch but that is redundant and you will essentially be repeating the logic.

Of course there are special cases where you do not need the implementations of the methods from the base/parent class and it is perfectly fine not to use the super().

Remember one of the important things about inheriting classes and methods is to essentially be able to write maintainable and reusable codes while you focus your attention on another logic.

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477502

There are a few reasons for this.

First of all, ListView can construct a QuerySet in two ways: by specifying a .model attribute, or a .queryset attribute. So if you would use self.queryset, it would not work for ListViews where you use the .model approach like:

from django.views.generic import ListView


class MyListView(ListView):
    model = MyModel

    #    🖟 will ***not*** work
    def get_queryset(self):
        return self.queryset.filter(initiator=self.request.user)

Secondly, the .get_queryset() of a MultipleObjectMixin, which is the mixin that gives a ListView the .get_queryset() method, also orders the queryset if .ordering is defined, or .get_ordering() returns something different from None.

You can also define your own mixins, for example with:

class ActiveItemsMixin:
    def get_queryset(self):
        return super().get_queryset().filter(active=True)

and mix it in a view with:

class MyListView(ActiveItemsMixin, ListView):
    # …

Then super().get_queryset() will thus return items that have active=True, and so you thus can "chain" filter operations and thus define useful mixins.


Note: Since PEP-3135 [pep], you don't need to call super(…) with parameters if the first parameter is the class in which you define the method, and the second is the first parameter (usually self) of the function.

Upvotes: 0

Vegard
Vegard

Reputation: 4927

Before you can return a filtered queryset, you need to have a queryset that can be filtered. So the first line fetches the queryset, by some way that's defined in a superclass. The second line then returns a filtered version of it.

def get_queryset(self):
    # 1. Get the original queryset
    queryset = super(OrderListView, self).get_queryset()

    # 2. Filter the queryset
    return queryset.filter(initiator=self.request.user)

Upvotes: 0

Related Questions