How to create new instances of both models contributing in OneToOne relationship in django rest framework (DRF)?

Viewed 56

I have created an Author model in which it has a OneToOne relationship with default Django User, as below:

from django.contrib.auth.models import User
from django.db import models


class Author(models.Model):
    user = models.OneToOneField(
        User,
        related_name='author',
        on_delete=models.CASCADE,
        default="",
    )

    is_author = models.BooleanField(
        default=True
    )

And here I have created the following viewset:

class AuthorViewSet(viewsets.ModelViewSet):
    serializer_class = AuthorSerializer
    queryset = Author.objects.all()

    def get_permissions(self):
        if self.action == "list" or self.action == "retrieve" or self.action == "update":
            self.permission_classes = [IsCurrentOwner, permissions.IsAdminUser]

        elif self.action == "create":
            self.permission_classes = [permissions.AllowAny]

        return super(AuthorViewSet, self).get_permissions()

Question Is there any way that I can create both user and author in one step (request)? How?

serializer code:

class AuthorSerializer(serializers.ModelSerializer):

    class Meta:
        model = Author
        fields = "__all__"

        extra_kwargs = {
            'password': {'write_only': True},
            'id': {'read_only': True}
        }

I have tried the following request but it doesn't work.

#url: localhost:8000/users/
#method: POST

{
    "user": {
        "username": "mostafa",
        "password": "1"
    }       
}

Error:

{
    "user": [
        "Incorrect type. Expected pk value, received dict."
    ]
}
1 Answers

For this, you will need to create another serializer for user:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"

        extra_kwargs = {
            'password': {'write_only': True},
        }

After that, make the user field in your AuthorSerializer an instance of UserSerializer and override the create method in this manner:

class AuthorSerializer(serializers.ModelSerializer):
    user = UserSerializer()
    
    class Meta:
        model = Author
        fields = "__all__"

        extra_kwargs = {
            'id': {'read_only': True}
        }

    def create(self, validated_data):
        user = None
        if "user" in validated_data:
            user_data = validated_data.pop("user") or {}
            user = User.objects.create_user(**user_data) # Assuming that it is default django user model
        author = Author.objects.create(user=user, **validated_data)
        author.save()
        return author

curl script for your request payload:

curl --location --request POST 'http://127.0.0.1:8000/users/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user": {
        "username": "mostafa",
        "password": "abcd"
    },
    "is_author": true
}'

And it responses as below:

{
    "id": 1,
    "user": {
        "id": 1,
        "last_login": null,
        "is_superuser": false,
        "username": "mostafa",
        "first_name": "",
        "last_name": "",
        "email": "",
        "is_staff": false,
        "is_active": true,
        "date_joined": "2020-12-05T09:54:37.674749Z",
        "groups": [],
        "user_permissions": []
    },
    "is_author": true
}
Related