1

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 :)

2 Answers 2

1

When you're using ModelSerializer, you do not need to define fields like,

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')

Remove these from both the serailizers. In your create method, you should consider sending user as a param of validated data

def create(self, validated_data):
    account_data = validated_data.pop('account', None)
    user = validate_data.pop('user')
    account = Account.objects.update_or_create(user=user, defaults=account_data)
    return account
Sign up to request clarification or add additional context in comments.

4 Comments

i try this and i get error "int() argument must be a string, a bytes-like object or a number, not 'dict'"
I hope you're sending a User model instance in validate_data and account_data is a dictionary.
hi sir, check my answer. do i use the right way ?
yeah it's work very very fine. but i still don't try for update. and i'm try to refactor the code. thank you sir for your attention :)
0

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  

Comments

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.