I’m currently implementing djangorestframework for my app RESTful API. After playing around with it, I still do not clearly understand what .create(self, validated_data)
and .update(self, validated_data)
used for in the serializer. As I understand, CRUD only calls the 4 main methods in viewsets.ModelViewSet
: create()
, retrive()
, update()
, and destroy()
.
I also have already tried to debug and print out stuff to see when the .create()
and .update()
methods are called in both ModelViewSet
and ModelSerializer
. Apparently, only the methods in ModelViewSet
are called when I do the HTTP verbs. However, for ModelSerializer
, I don’t see any calls in those 2 methods. I just want to know what are those methods used for in ModelSerializer
since I see that people override those methods a lot in the serializer.
Advertisement
Answer
You really must split things between the views and the serializer.
Serializers
The Serializer
is a standalone object. It is used for converting a Django model (or any kind of python datastructure, actually) into a serialized form, and the other way around.
You may use it as such, wherever you want. It does not even need an actual HTTP request as long as you don’t need URIs in your output.
The ModelSerializer
subclass is a specialized kind of Serializer
that adds “load-from-model” and “save-to-model” functionality.
The “save-to-model” entry point is the save()
method. For easier overriding, its default implementation will delegate its work to either the create()
or update()
method of the serializer, depending on whether it is creating a new model instance, or updating one.
The purpose of that is customization: it gives you, the developer, the option to override just the create method, just the update method, or common behavior. For instance, it allows you to do this kind of things:
def save(self, **kwargs): # Will be done on every save kwargs['last_changed'] = timezone.now() return super().save(**kwargs) def create(self, instance, data): # Will only be done if a new object is being created data['initial_creation'] = timezone.now() return super().create(instance, data)
That’s a basic example. There, the last_changed
field will be set every time an object is saved, be it a creation or an update.
As a sidenote, you probably do not want to do that. Things such as setting “last_changed” fields should live in the view, not in the serializer.
Viewsets
In a completely different place, Django REST framework supplies Viewsets
. Those are an organized collection of views, revolving around implementing a CRUD API for a model.
As such, it structures it functionality into a set of methods, namely create()
, retrieve()
/list()
, update()
and delete()
.
The main point being: there is no connection whatsoever between the viewset’s create()
method and the serializer’s create()
method.
It just happens that the default implementation of the viewset’s methods uses a ModelSerializer
and that the default implementation of that serializer’s save()
method delegates the job to methods that have the same name.
By the way, about the last_changed
example, here is how you would do it in the view:
def perform_create(self, serializer): now = timezone.now() serializer.save(initial_creation=now, last_changed=now) def perform_update(self, serializer): serializer.save(last_changed=timezone.now())
That’s functionally equivalent to the example above, but lives in the viewset.
Conclusion
So back to your question, the specific thing you should override depends on which object is responsible for the task you want to add.
- If your custom behavior is part of the serialization process, that is, the process of converting raw data back into a proper Django model and saving it, then you should override the
Serializer
‘s methods. - If, on the other hand, your custom behavior is specific to your viewset, then you should override the
Viewset
‘s methods.
As a hint, you may ask yourself the following question: if I use the same serializer in another place (maybe another viewset), should it always display that behavior?