saran3h
saran3h

Reputation: 14082

Django Serializer: How can I validate a model instance?

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

Answers (2)

saran3h
saran3h

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

Enthusiast Martin
Enthusiast Martin

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

Related Questions