When to use Serializer's create() and ModelViewset's create() perform_create()
I want to clarify the given documentation of
django-rest-framework regarding the creation of a model object. So far I found that there are 3 approaches on how to handle such events.
- The Serializer's
create()method. Here is the documentation
class CommentSerializer(serializers.Serializer): def create(self, validated_data): return Comment.objects.create(**validated_data)
- The ModelViewset
class AccountViewSet(viewsets.ModelViewSet): queryset = Account.objects.all() serializer_class = AccountSerializer permission_classes = [IsAccountAdminOrReadOnly]
- The ModelViewset
class SnippetViewSet(viewsets.ModelViewSet): def perform_create(self, serializer): serializer.save(owner=self.request.user)
These three approaches are important depending on your application environment.
But WHEN do we need to use each
create() / perform_create() function??. On the other hand I found some account that two create methods were called for a single post request the modelviewset's
create() and serializer's
Hopefully anyone would share some of their knowledge to explain and this will surely be very helpful in my development process.
- You would use
create(self, validated_data)to add any extra details into the object before saving AND "prod" values into each model field just like
**validated_datadoes. Ideally speaking, you want to do this form of "prodding" only in ONE location so the
createmethod in your
CommentSerializeris the best place. On top of this, you might want to also call external apis to create user accounts on their side just before saving your accounts into your own database. You should use this
createfunction in conjunction with
ModelViewSet. Always think - "Thin views, Thick serializers".
def create(self, validated_data): email = validated_data.get("email", None) validated.pop("email") # Now you have a clean valid email string # You might want to call an external API or modify another table # (eg. keep track of number of accounts registered.) or even # make changes to the email format. # Once you are done, create the instance with the validated data return models.YourModel.objects.create(email=email, **validated_data)
create(self, request, *args, **kwargs)function in the
ModelViewSetis defined in the
CreateModelMixinclass which is the parent of
CreateModelMixin's main functions are these:
from rest_framework import status from rest_framework.response import Response def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer): serializer.save()
As you can see, the above
create function takes care of calling validation on your serializer and producing the correct response. The beauty behind this, is that you can now isolate your application logic and NOT concern yourself about the mundane and repetitive validation calls and handling response output :). This works quite well in conjuction with the
create(self, validated_data) found in the serializer (where your specific application logic might reside).
- Now you might ask, why do we have a separate
perform_create(self, serializer)function with just one line of code!?!? Well, the main reason behind this is to allow customizeability when calling the
savefunction. You might want to supply extra data before calling
serializer.save(owner=self.request.user)and if we didn't have
perform_create(self, serializer), you would have to override the
create(self, request, *args, **kwargs)and that just defeats the purpose of having mixins doing the heavy and boring work.
Hope this helps!