I'm following along with a Django Rest Framework tutorial (source code here) and I have a few questions about the below code snippet:
class ReviewCreate(generics.CreateAPIView):
serializer_class = ReviewSerializer
permission_classes = [IsAuthenticated]
throttle_classes = [ReviewCreateThrottle]
def get_queryset(self):
return Review.objects.all()
def perform_create(self, serializer):
pk = self.kwargs.get('pk')
watchlist = WatchList.objects.get(pk=pk)
review_user = self.request.user
review_queryset = Review.objects.filter(watchlist=watchlist, review_user=review_user)
if review_queryset.exists():
raise ValidationError("You have already reviewed this movie!")
if watchlist.number_rating == 0:
watchlist.avg_rating = serializer.validated_data['rating']
else:
watchlist.avg_rating = (watchlist.avg_rating serializer.validated_data['rating'])/2
watchlist.number_rating = watchlist.number_rating 1
watchlist.save()
serializer.save(watchlist=watchlist, review_user=review_user)
- In the class definition, the variable
serializer_class
is declared; however in theperform_create
method,serializer
is an argument. Given the differences in naming, how are these two related? - In the method
perform_create
,self.kwargs
is referenced. However, I don't see a kwargs argument passed to any__init__
method or else attached to the class object. How/where is kwargs passed to the class?
In both cases, I can only assume that the inherited class (generics.CreateAPIView
) has an __init__
method that assigns a serializer_class
variable to serializer
. How it "listens" for a child class definition of serializer_class
, I have no idea. And as for kwargs, I'm at a loss for how this is passed to the child class w/o explicitly calling defining it in its arguments.
Edit, this question Kwargs in Django does not answer my question-- it just explains what keyword arguments are. I'm not confused about their name, I'm confused by their invisible yet implicit reference in this code.
CodePudding user response:
Answering your first point, we have to note two things:
First, the method
perform_create
is used in thecreate
method associated toCreateModelMixin
(see https://github.com/encode/django-rest-framework/blob/71e6c30034a1dd35a39ca74f86c371713e762c79/rest_framework/mixins.py#L16). The classCreateAPIView
inherits from this mixin and also fromGenericAPIView
(See https://github.com/encode/django-rest-framework/blob/b1004a47334a0dd1929e6d50b8f7ff6badc959f4/rest_framework/generics.py#L184). As you can see, thecreate
method mentioned above uses the classperform_create
method and needs a serializer there. Definingperform_create
without that argument would lead to an error when creating objects with this method.Another thing to note is that the serializer used comes from the
get_serializer
method. Checking the source code forGenericAPIView
(https://github.com/encode/django-rest-framework/blob/b1004a47334a0dd1929e6d50b8f7ff6badc959f4/rest_framework/generics.py#L103) we can see that this method callsget_serializer_class
which retrieves the serializer defined byserializer_class
.
In conclusion, if you don't modify anything else, the serializer
that will be passed as a parameter will be an instance of you serializer class defined in serializer_class
.
Getting to your second point, if you try to search the parent class of GenericAPIView
and follow on searching the base class from which these classes inherit, you will end up finding that the base class is View
from django.views.generic
. There you will find in the setup
method (https://github.com/django/django/blob/27aa7035f57f0db30b6632e4274e18b430906799/django/views/generic/base.py#L124) where the kwargs
attribute is initialized. Also you can see in this method's code documentation the following statement:
"""Initialize attributes shared by all view methods."""
Thus in any view we create (if it has View
as its base class) we will always be able to manipulate self.request
, self.args
and self.kwargs
. I hope I explained myself clearly!