FlashBanistan
FlashBanistan

Reputation: 456

How do I use the same model serializer for my Viewset's 'create' and 'update' methods

When a new client is created, I need them to send back 'confirm_domain_url' so that I can do a check on whether or not it equals 'domain_url'. I call serializer.is_valid() in the create method and it works as intended. When a client is updated, it fails saying that 'domain_url' is missing. I know I can just create a new serializer for the update method but I would like to consolidate as much code as I can.

Serializer:

class ClientSerializer(ModelSerializer):
    confirm_domain_url = CharField(write_only=True)
    class Meta:
        model = Client
        fields = [
            'name',
            'schema_name',
            'domain_url',
            'created_on',
            'id',
            # Write only fields (won't be returned in the response):
            'confirm_domain_url',
        ]

    def validate(self, data):
        # Check that domain urls match:
        if data['domain_url'] != data['confirm_domain_url']:
            raise ValidationError('Domain urls do not match.')
        return data

    def create(self, validated_data):
        client = Client(
            name = validated_data['name'],
            schema_name = validated_data['schema_name'],
            domain_url = validated_data['domain_url'],
        )
        client.save()

        return client

ViewSet:

class ClientViewSet(viewsets.ViewSet):

    def list(self, request):
        queryset = Client.objects.all()
        serializer = ClientSerializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        queryset = Client.objects.all()
        client = get_object_or_404(queryset, pk=pk)
        serializer = ClientSerializer(client)
        return Response(serializer.data)

    def create(self, request):
        serializer = ClientSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)

    def update(self, request, pk=None):
        queryset = Client.objects.all()
        client = queryset.get(pk=pk)
        serializer = ClientSerializer(client, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)

Upvotes: 0

Views: 48

Answers (1)

A. J. Parr
A. J. Parr

Reputation: 8026

We can add a check to the ClientSerializer.validate method which checks if the instance attribute has been assigned to the serializer. If an instance has been defined on the serializer then it would be an update operation, otherwise it would not have an instance to update. So this will only check domain_url against confirm_domain_url when creating.

class ClientSerializer(ModelSerializer):
    ...

    def validate(self, data):
        # Check that domain urls match:
        if self.instance is None and data['domain_url'] != data['confirm_domain_url']:
            raise ValidationError('Domain urls do not match.')
        return data

    ...

edit - I changed the logical order of the if statement in the validate method. The problem was it was trying to access data['domain_url'] in the first part of the conditional, leading to a KeyError. Now that it checks for self.instance is None first, this will short-circuit the if statement and it wont try to access data['domain_url'] if an instance is defined.

Upvotes: 2

Related Questions