When to use Serializer's create() and ModelViewset's perform_create()
Solution 1
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_data
does. Ideally speaking, you want to do this form of "prodding" only in ONE location so thecreate
method in yourCommentSerializer
is 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 thiscreate
function in conjunction withModelViewSet
. Always think - "Thin views, Thick serializers".Example:
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)
The
create(self, request, *args, **kwargs)
function in theModelViewSet
is defined in theCreateModelMixin
class which is the parent ofModelViewSet
.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 thecreate(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 thesave
function. You might want to supply extra data before callingsave
(likeserializer.save(owner=self.request.user)
and if we didn't haveperform_create(self, serializer)
, you would have to override thecreate(self, request, *args, **kwargs)
and that just defeats the purpose of having mixins doing the heavy and boring work.
Solution 2
While Apoorv's answer is correct and very detailed, here's a quick answer:
- Override
perform_create()
when you want to change the "behind-the-scenes" behavior of how your object is created. For example, performing some extra actions before or after the object is created. - Override
create()
when you want to modify the response. For example, if you want to re-structure the response, add extra data, extra headers, etc.
Comments
-
Roel almost 2 years
I want to clarify the given documentation of Django-rest-framework regarding the creation of a model object. So far I have found that there are 3 approaches on how to handle such events.
-
The Serializer's
create()
method. Here is the documentationclass CommentSerializer(serializers.Serializer): def create(self, validated_data): return Comment.objects.create(**validated_data)
-
The ModelViewset
create()
method. Documentationclass AccountViewSet(viewsets.ModelViewSet): queryset = Account.objects.all() serializer_class = AccountSerializer permission_classes = [IsAccountAdminOrReadOnly]
-
The ModelViewset
perform_create()
method. Documentationclass 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 accounts that two create methods were called for a single post request theModelViewSet
'screate()
and serializer'screate()
. -
-
Roel over 7 yearsHi! Thanks for your sharing your knowledge! About the
create(self, validated_data)
in the serializer, it means that it focuses on data validation logic? and more over it can help return the given serializer's data back to the response right? -
Apoorv Kansal over 7 yearsNo so at this point, you already have passed all your validation. I am talking about how you might want to customize the validated data just before it is saved into a database. I will make an example in my answer.
-
Apoorv Kansal over 7 yearsNo worries - just added an example to give more context.
-
Roel over 7 yearsThen this code
return models.YourModel.objects.create(email=email, **validated_data)
will save the data from database right? -
Apoorv Kansal over 7 yearsYeah that is the final line that will save your object into the database
-
Roel over 7 yearsLet us continue this discussion in chat.
-
Roel over 7 yearsi have another question again because there is some approach which calls the
objects.create
both found in theserializer
andmodelviewset
. here is an example:class AccountSerializer(serializers.ModelSerializer): def create(self, validated_data): return Account.objects.create(**validated_data)
Andclass AccountViewSet(viewsets.ModelViewSet): def create(self, request): serializer = self.serializer_class(data=request.data) if serializer.is_valid(): Account.objects.create_user(**serializer.validated_data)
-
Roel over 7 yearsI could interpret this one which creates double account but when tested it's only creates a single instance at all. Which one is called first? i am very confused at this.
-
Apoorv Kansal over 7 yearsSo the
create
function in the serializer itself is only called when you doserializer.save()
. In yourcreate(self, request)
function inside (AccountViewSet
), you are not callingserializer.save()
at all and therefore, the only instance creation is happening with this call:Account.objects.create_user(**serializer.validated_data)
. -
qg_java_17137 over 6 yearsIs there
create()
andperform_create()
has execute order? -
Roel over 6 years@qg_java一坨子给你中疼 it depends on the situation, since perform_create can also be called in another definition (def:) such as update, delete, patch etc.. it means there is no execution order or precedence and it heavily relying on your own logic.
-
Gonzalo about 5 yearswhat about validation of fields? Suppose I have two numbers/fields and I want to check that one is less than the other.. where would you put all that field validation logic? in the view? What about the full_clean method of the model? (So it can be used by the api and admin for example
-
Josh almost 3 yearsHi is it possible to do a get_or_create when performing the perform_create? Or is there another recommendation?
-
Haliax over 2 yearsWhich create() method? The one that belongs to serializer or to ModelViewSet?