2

I am trying to post a message to the database using the following content on the request:

{
    "thread": 1,
    "content": "lorem ipsum",
    "author": 
    {
        "name":"doc",
        "email":""
    }

}

Instead I get the error message of "Cannot assign "OrderedDict([(u'name', doc'), (u'email', u'')])": "Message.author" must be a "Author" instance."

Each Message has a related author record, that may or may not exist already in the database. At this point I don't mind having repeat entries on that table.

My serializers.py file has the following:

class AuthorSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=False, required=False)
    name = serializers.CharField(required=True, max_length=50)
    email = serializers.CharField(allow_blank=True, allow_null=True, required=False)

    def create(self, validated_data):
        """
        Create and return a new `Author` instance, given the validated data.
        """
        return Author.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Author` instance, given the validated data.
        """
        instance.name = validated_data.get('name', instance.name)
        instance.email = validated_data.get('email', instance.email)
        instance.save()
        return instance

class Meta:
    model = Author
    fields = ('name', 'email')

class MessageSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    thread = serializers.PrimaryKeyRelatedField(queryset=Thread.objects.all())
    created_at = serializers.DateTimeField(required=False)
    content = serializers.CharField(style={'base_template': 'textarea.html'})
    author = AuthorSerializer(required=False, read_only=False)

    def create(self, validated_data):
        """
        Create and return a new `Message` instance, given the validated data.
        """
        return Message.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Message` instance, given the validated data.
        """
        instance.thread = validated_data.get('thread', instance.thread)
        instance.content = validated_data.get('content', instance.content)
        instance.author = validated_data.get('author', instance.author)
        instance.save()
        return instance

    class Meta:
        model = Message
        fields = ('thread', 'created_at', 'content', 'author')

I'm quite lost on how I should proceed, has I expected that AuthorSerializer would be able to parse a list or ordered dictionary into a new or existing object.

1 Answer 1

15

According to the docs, you have to create the nested object in your custom create method:

class MessageSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    thread = serializers.PrimaryKeyRelatedField(queryset=Thread.objects.all())
    created_at = serializers.DateTimeField(required=False)
    content = serializers.CharField(style={'base_template': 'textarea.html'})
    author = AuthorSerializer(required=False, read_only=False)

    def create(self, validated_data):
        """
        Create and return a new `Message` instance, given the validated data.
        """
        author_data = validated_data.pop('author', None)
        if author_data:
            author = Author.objects.get_or_create(**author_data)[0]
            validated_data['author'] = author
        return Message.objects.create(**validated_data)
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you Mark, that was a great help. I ended up changing the code a bit because it raising an error saying "author" had not been referenced before. final code also uses get_or_create(), Author.objects.get_or_create(**author_data)[0]
updated to reflect. Oh I see my error with author, sorry. Fixed :)
take care of the cases where you have an author object created but no message object created, in case message object creation fails for some reason.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.