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_classis declared; however in theperform_createmethod,serializeris an argument. Given the differences in naming, how are these two related? - In the method
perform_create,self.kwargsis 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_createis used in thecreatemethod associated toCreateModelMixin(see https://github.com/encode/django-rest-framework/blob/71e6c30034a1dd35a39ca74f86c371713e762c79/rest_framework/mixins.py#L16). The classCreateAPIViewinherits 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, thecreatemethod mentioned above uses the classperform_createmethod and needs a serializer there. Definingperform_createwithout 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_serializermethod. 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_classwhich 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!
