Reputation: 21
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
Reputation: 1
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
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 ListView
s 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 (usuallyself
) of the function.
Upvotes: 0
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