Sebastian Olafsson
Sebastian Olafsson

Reputation: 3

Nested serializer using foreign key with only one object in DRF

I'm using Django Rest Framework with two models, a service and a message, where a service can have many messages. Using a foreign key.

class Service(models.Model):
    servicename = models.TextField(blank=False)
    sender = models.TextField(blank=False)


class Message(models.Model):

    service = models.ForeignKey(Service, related_name='messages', on_delete=models.CASCADE)
    description = models.TextField(blank=False)

The only usecase is where you enter a service, which is nested to take input for the message. Therefore, service will create both the service and the message at the same time. If the service doesn't exist, it will be created. If it does exist, the message will point towards the service.

As it is a one-to-many, it seems like I need many=True in my serializer, which is undesirable as there is never a usecase where two messages are inputted simultaneously, and also means the web UI will not work since lists are not supported.

class MessageSerializer(serializers.ModelSerializer):

    class Meta:
        model = Message
        fields = ('description',)


class ServiceSerializer(serializers.ModelSerializer):

    messages = MessageSerializer(many=True)

    class Meta:
        model = Service
        fields = ('servicename', 'sender', 'messages')

    def create(self, validated_data):
        messages_data = validated_data.pop('messages')
        print(messages_data)
        service, created = Service.objects.get_or_create(
            servicename=validated_data['servicename'],
            sender=validated_data['sender']
        )
        Message.objects.create(service=service, **messages_data[0])
        return service

So, is it possible to have a nested serializer with only one object during input? Removing many=True results in a typical AttributeError, which I've come to understand means it is not allowed since the reverse of one-to-many means it needs to have the ability to take multiple objects.

Thanks

Upvotes: 0

Views: 3481

Answers (1)

Enthusiast Martin
Enthusiast Martin

Reputation: 3091

I would suggest for you to use two different fields for this.

Something like this:

class ServiceSerializer(serializers.ModelSerializer):

    messages = MessageSerializer(many=True, read_only=True)
    message = MessageSerializer(write_only=True)

So, for input - you will have only write_only attribute which won't be serialized on the way out - only when you post ( you can decide with required attribute to make it optional )

And vice versa for messages - only read only.

And you handle it accordingly in create method.

Upvotes: 1

Related Questions