Reputation: 14082
I'm pretty new to Django Serializers and still confused on how they work.
I have a fairly general scenario wherein I'm calling my api which simply sets a field in my model object and saves it (assume the record is already present and it is just being updated). However, I need to validate this model object before saving it.
api.py
@detail_route(methods=['POST'], url_path='submit-draft')
def submit_draft(self, request, *args, **kwargs):
booking = self.get_object()
# serializer with custom validations.
serializer = self.get_serializer(booking)
serializer.is_valid(raise_exception=True)
booking.submit_draft(by=request.user)
booking.save()
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Ok(data)
serializers.py
class BookingCreateUpdateSerializer(serializers.ModelSerializer):
date = serializers.CharField()
duration = serializers.IntegerField(required=True, )
created_by = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault(), )
modified_by = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault(), )
....
class Meta:
model = models.Booking
fields = [
'title',
'date',
'duration',
'client',
'created_by',
'modified_by',
....
]
However, I get this error:
AssertionError: Cannot call '.is_valid()' as no 'data=' keyword argument was passed when instantiating the serializer instance.
I understand that the serializer is expecting a dictionary rather than the actual modal object. But I can't figure out how to achieve what I want to ie. validate a model object. Can anyone please suggest the right approach?
Upvotes: 2
Views: 7734
Reputation: 14082
This is what finally worked after spending almost a day scratching my head.
api.py
@detail_route(methods=['POST'], url_path='submit-draft')
def submit_draft(self, request, *args, **kwargs):
# Step1. Get current model instance.
booking = self.get_object()
# Step2. Serialize the model instance using same serializer as in Step5.
booking_data = serializers.BookingDetailSerializer(booking, context={'request':
request}).data
# Step3. Deserialize the data from Step2 (using `BookingCreateUpdateSerializer` serializer
as mentioned in description and check for validations.
serializer = self.get_serializer(data=booking_data)
serializer.is_valid(raise_exception=True)
# Step4. If no validation error, set the required field and save the instance.
booking.submit_draft(by=request.user)
booking.save()
# Step5. Return response.
data = serializers.BookingDetailSerializer(booking, context={'request': request}).data
return response.Ok(data)
I learnt something more about the serializers today. We can pass around model instances into different serializers and change its shape as per our requirements.
Upvotes: 0
Reputation: 3091
You can use serializer to:
1) serialize your object to a dictionary. In this case, you dont need to call is_valid as the object has been already created and it has valid values. So you can do the following:
serializer = BookingCreateUpdateSerializer(booking)
return Response(serializer.data)
2) serialize an input and create new object using the serializer. In this case, input is a dictionary and it needs to be validated first before calling save. And you can do something like this:
serializer = BookingCreateUpdateSerializer(data=input_dict)
serializer.is_valid()
serializer.save()
Update to answer the comment
You can do the following to update an instance using the serializer:
serializer = BookingCreateUpdateSerializer(booking, data=input_data_to_update, partial=True)
serializer.is_valid()
instance = serializer.save()
# and serialize the updated instance for response using another serializer
output_data = BookingDetailSerializer(instance).data
Upvotes: 5