Search code examples
djangopostdjango-rest-frameworkone-to-onedjango-serializer

Django Create Extended User with Serializer


I'm working on extending the Basic User model to additional fields. I had created a Profile Model that should have a OneToOneRelation. I'm working with serializers. Now when I try to post a dummy user, I get this error:

**TypeError: User() got an unexpected keyword argument 'street'**

If I send only the user it works well. I know that the 'street' argument is not part of the User, but part of the Profile that should store later.

I tried to solve this with 'request.POST.pop' for every value and parsed to dict but then no data will be transmitted. Additionally, I had no success with Signals.

Does anyone have an idea how I can make this work, as the user and profile will be created at the same time? The user must save first and pass the id its generating to the Profile that is referenced to it.

Models.py:

class Profile(models.Model):

    user = models.OneToOneField(User, on_delete=CASCADE, null=True)
    street = models.CharField(name="street", max_length=100)
    number = models.CharField(name="number", max_length=10)
    plz = models.CharField(name="plz", max_length=10)
    city = models.CharField(name="city", max_length=100)
    phone = models.CharField(name="phone", max_length=20)
    locked = models.BooleanField(name="locked", default=False)

Serializer.py:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = '__all__'

class ProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = Profile
        fields = ['street', 'number', 'plz', 'city', 'phone']

Views.py:

@api_view(['POST'])
def userCreate(request):

    userSerializer = UserSerializer(data=request.data)
    if userSerializer.is_valid():
        user = userSerializer.create(validated_data=request.data)
        profileSerializer = ProfileSerializer(instance=user ,data=request.data)
        if profileSerializer.is_valid():
            profileSerializer.create()
        return Response(status=status.HTTP_200_OK)
    return Response(status=status.HTTP_400_BAD_REQUEST)

Solution

  • You can rewrite the serializer to include profile. And then override the create method.

    class UserSerializer(serializers.ModelSerializer):
        profile = ProfileSerializer()
    
        class Meta:
            model = User
            fields = '__all__'
    
        def create(self, validated_data):
            profile_data = validated_data.pop('profile')
            user = User.objects.create(**validated_data)
            Profile.objects.create(**profile_data, user=user)
            return user
    

    Then your view becomes:

    @api_view(['POST'])
    def userCreate(request):
    
        userSerializer = UserSerializer(data=request.data)
        if userSerializer.is_valid():
            user = userSerializer.save()
            return Response(status=status.HTTP_200_OK)
        return Response(status=status.HTTP_400_BAD_REQUEST)
    

    The request to server should then be modified as:

    {
     profile: {
      ... // profile specific attributes like street, number, city
     },
    ... // user specific attibutes
    }
    

    PS: To update the instance, you should override the update method of serializer.