Search code examples
pythondjangopostserializationdjango-rest-framework

Create Account, nested serialization Django Rest Framework


Hello guys i'm back again
i have a little trouble about my problem.

I want to create Account in my Django Project. I extend user model using OneToOneField.

here is my mode.py

class Account(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    phone_number = models.CharField(max_length=15, unique=True, null=False, blank=False)
    birth_date = models.DateField()
    address = models.TextField()
    district = models.ForeignKey(District, on_delete=models.CASCADE, related_name='district')
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['created_at', ]

    def __str__(self):
        return self.user.username

and here is my serializers.py

class UserSerializer(serializers.ModelSerializer):

    id = serializers.IntegerField(source='account.id', read_only=True)
    phone_number = serializers.CharField(source='account.phone_number')
    birth_date = serializers.DateField(source='account.birth_date')
    address = serializers.CharField(source='account.address')

    class Meta:
        model = User
        fields = (
            'id', 'first_name',
            'last_name', 'username',
            'email', 'password',
            'phone_number', 'birth_date',
            'address'
        )

    def update_or_create_account(self, user, account_data):
        Account.objects.update_or_create(user=user, defaults=account_data)

    def create(self, validated_data):
        account_data = validated_data.pop('account', None)
        user = super(UserSerializer, self).create(validated_data)
        self.update_or_create_account(user, account_data)
        return user

    def update(self, instance, validated_data):
        account_data = validated_data.pop('account', None)
        self.update_or_create_account(instance, account_data)
        user = super(UserSerializer, self).update(instance, validated_data)
        return user


class AccountSerializer(serializers.ModelSerializer):
    user = UserSerializer(required=True)
    created_at = serializers.DateTimeField(read_only=True)

    class Meta:
        model = Account
        fields = (
            'user',
            'district', 'created_at'
        )

and here for views.py

class AccountList(APIView):

    def get(self, format=None):
        account = Account.objects.all()
        serializer = AccountSerializer(account, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = AccountSerializer(data=request.data)
        if User.objects.filter(email=request.data['user']['email']).exists():
            return Response(data={'email': 'Email already taken, use another email'}, status=status.HTTP_400_BAD_REQUEST)
        if serializer.is_valid():
            serializer.create(validated_data=request.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.error_messages,
                        status=status.HTTP_400_BAD_REQUEST)

and when i try to POST some Account. i got this error
The ".create()" method does not support writable nested fields by default. Write an explicit ".create()" method for serializer "account.serializers.AccountSerializer", or set "read_only=True" on nested serializer fields.

can someone help me ? i don't really understand about this error
Thank you :)


Solution

  • i have answer for my question.

    class UserSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = User
            fields = ('username', 'email', 'password', 'first_name', 'last_name')
    
    
    class AccountSerializer(serializers.ModelSerializer):
    
        user = UserSerializer()
        created_at = serializers.DateField(read_only=True)
    
        class Meta:
            model = Account
            fields = ('user', 'phone_number', 'birth_date', 'address', 'district', 'created_at')
    
        def create(self, validated_data):
            user_data = validated_data.pop('user')
            user = User.objects.create(
                username=user_data['username'], 
                email=user_data['email'], 
                first_name=user_data['first_name'], 
                last_name=user_data['last_name']
            )
            user.set_password(user_data['password'])
            user.save()
            account = Account.objects.create(
                user=user,
                phone_number=validated_data['phone_number'],
                birth_date=validated_data['birth_date'],
                address=validated_data['address'],
                district=validated_data['district']
            )
            return account