Ankit Arora
Ankit Arora

Reputation: 97

Saving User details in UserDetail Table using One-To-One Relation Django Rest API

I am new to django. I am trying to save User details in another table using One-To-One relation by following Django document but is giving me an error

Models.py

class UserDetails(models.Model):
    user=models.OneToOneField(User,on_delete=models.CASCADE)
    phone=models.CharField(max_length=10)
    address=models.CharField(max_length=200,default="")
    created=models.DateTimeField(auto_now_add=True)
    updated=models.DateTimeField(auto_now_add=True)
    objects=models.Manager()
    def __str__(self):
         return self.user.username

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
         UserDetails.objects.create(user=instance,phone="",address="")
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.userdetails.save()

Serializer.py

class UserDetailsSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserDetails
        fields= ['phone','address']
class CreateUserSerializer(serializers.ModelSerializer):
    user=UserDetailsSerializer()
    class Meta:
        model =User
        fields=['id','url','username','email','password','user']

    def create(self, validated_data):
        user_data=validated_data.pop('user')
        user=User.objects.create(**validated_data)
        UserDetails.objects.create(user=user,**user_data)          
    return user

When i write above in serializer.py user=UserDetailsSerializer(read_only=True) else give me following error

Got AttributeError when attempting to get a value for field user on serializer CreateUserSerializer. The serializer field might be named incorrectly and not match any attribute or key on the User instance. Original exception text was: 'User' object has no attribute 'user'.

I found one way to make it work but I have to define every field manually but I want the above serializer to work

Working Serializer.py

class CreateUserSerializer(serializers.HyperlinkedModelSerializer):
    last_login=serializers.ReadOnlyField()
    date_joined=serializers.ReadOnlyField()
    phone=serializers.CharField(source='userdetails.phone')
    address=serializers.CharField(source='userdetails.address')
    updated=serializers.ReadOnlyField(source='userdetails.updated')
    # password=serializers.CharField(style={'input_type':'password'},write_only=True)
    class Meta:
        model = User
        fields=['id','first_name','last_name','username','password','phone','address','url','email','is_superuser','is_staff','last_login','date_joined','updated']
    extra_kwargs={
        'password':{'write_only':True},
    }

def create(self, validated_data):
    userdetails_data=validated_data.pop('userdetails')
    user=User.objects.create_user(**validated_data)
    user.userdetails.phone=userdetails_data.get('phone')
    user.userdetails.address=userdetails_data.get('address')
    user.save()
    return user

Edit1: As suggested by bdbd in comments I replaced user with userdetails in CreateUserSeriailzer it solved Attribute Error but was giving an error while creating user:

(1062, "Duplicate entry '106' for key 'apis_userdetails.user_id'")

So I replaced UserDetails.objects.create(user=user,**user_data) with user.userdetails.phone=user_data.get('phone') user.userdetails.address=user_data.get('address')

If anyone knows how can I minimize this code further please let me know because I need to create a lot of columns in UserDetails Table so I don't want to add each value in each column manually

Updated Serializer.py (1st One)

class CreateUserSerializer(serializers.ModelSerializer):
    #user=UserDetailsSerializer()
    userdetails=UserDetailsSerializer()
    class Meta:
        model =User
        fields=['id','url','username','email','password','userdetails']

    def create(self, validated_data):
        user_data=validated_data.pop('userdetails')
        user=User.objects.create_user(**validated_data)
        # UserDetails.objects.create(user=user,**user_data)
        user.userdetails.phone=user_data.get('phone')
        user.userdetails.address=user_data.get('address')
        user.save()
        return user

Upvotes: 1

Views: 496

Answers (1)

Brian Destura
Brian Destura

Reputation: 12068

Try with this: If you choose not to use signals:

def create(self, validated_data):
    userdetails_data = validated_data.pop('userdetails')
    user = User.objects.create_user(**validated_data)
    UserDetails.objects.create(user=user, **userdetails_data)
    return user

If you choose to use signals:

def create(self, validated_data):
    user_data = validated_data.pop('userdetails')
    user = User.objects.create_user(**validated_data)
    user.userdetails.phone = user_data.get('phone')
    user.userdetails.address = user_data.get('address')
    user.userdetails.save()
    return user

Upvotes: 1

Related Questions