1

I need help with a PATCH request using Django rest framework.

I have a User model that inherits from AbstractBaseUser which has 2 fields: name and email. The email field is unique.

Then I have a DojoMaster model that has a OneToOne relationship with the User model:

models.py

class DojoMaster(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
    phone = models.BigIntegerField()
    username = models.CharField(max_length=100, unique=True)
    country = models.ForeignKey(Country, on_delete=models.CASCADE)

I am able to POST a "dojomaster" to the application. Let's say the "dojomaster" POST looks like this:

POST payload

{
    "user": {
        "name": "XYZ",
        "email": "xyz@mail.com",
        "password": "2He$8Mv*"
    },
    "phone": 2685211,
    "country": 575,
    "username": "iAmXyZ"
} 

Now the "dojomaster" wants to change some of these details, so a PATCH request is sent:

PATCH payload

{
    "user": {
        "name": "XYZ",
        "email": "xyz24@mail.com", #change email
        "password": "p@55w0rd" #change password
    },
    "phone": 3972925, #change phone number
    "country": 575,
    "username": "iAmXyZ"
} 

To achieve this, I created the following in my serializers.py:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('name', 'email', 'password')

class DojoMasterUpdateSerializer(serializers.ModelSerializer):
    user = UserSerializer(required=True)

    def update(self, instance, validated_data):
        user_data = validated_data.pop('user')
        user = User.objects.update(**user_data)
        instance.user = validated_data.get('user', user)
        instance.username = validated_data.get('username', instance.username)
        instance.phone = validated_data.get('phone', instance.phone)
        instance.country = Country.objects.get(
            country=validated_data.get('country', instance.country)
        )
        instance.save()
        return instance

    class Meta:
        model = DojoMaster
        fields = ('user', 'country', 'phone', 'username')
        write_only_fields = ('password',)

To use the serializers, I created the following view:

views.py

class DojoMasterUpdateView(generics.UpdateAPIView):
    queryset = DojoMaster.objects.all()
    serializer_class = DojoMasterUpdateSerializer

However, when I do this, I get a Status: 400 Bad Request error with the following payload:

{
    "user": {
        "email": [
            "user with this email already exists."
        ]
    }
}

I also tried doing with using a PUT request to no success.

How do I PATCH an entity with this type of OneToOneField relationship using DRF? Your help will be much appreciated.

dot64dot
  • 531
  • 1
  • 6
  • 15

1 Answers1

2

This question is old and you might have already found your answer.

However, if anybody wonders:

the issue is this:

user = User.objects.update(**user_data)

With this, you are not updating one instance of user - you are trying to update all users in the User table with user_data.

Since you are patching the instance of DojoMaster, you should already have the instance of user which belongs to it:

So, your update method can look like this instead:

def update(self, instance, validated_data):
    user_data = validated_data.pop('user')
    user_ser = UserSerializer(instance=instance.user, data=user_data)
    if user_ser.is_valid():
        user_ser.save()
    instance.username = validated_data.get('username', instance.username)
    instance.phone = validated_data.get('phone', instance.phone)
    instance.save()
    return instance
Enthusiast Martin
  • 3,041
  • 2
  • 12
  • 20