Direct assignment to the reverse side of a related set is prohibited. Use addresses.set() instead
Asked Answered
C

3

15

I am going to create and update nested relationships but it is not working as expected I have a three models User, Profile and Address. Profile model is a FK in Address model

profiles/models.py

class Address(models.Model):
   profile = models.ForeignKey(Profile, related_name='addresses', on_delete=models.CASCADE)
   country = models.CharField(max_length=255)
   city = models.CharField(max_length=255)
   state = models.CharField(max_length=100)
   zip_code = models.CharField(max_length=50)
   address = models.CharField(max_length=1000)

profiels/serializers.py

class ProfileSerializer(serializers.ModelSerializer):
    addresses = AddressSerializer(many=True)

class Meta:
    model = Profile
    fields = ['gender', 'date_of_birth', 'hometown', 'phone', 'addresses']

def create(self, validated_data):
    addresses_data = validated_data.pop('addresses')
    profile = Profile.objects.create(**validated_data)
    for address_data in addresses_data:
        Address.objects.create(profile=profile, **address_data)
    return profile

users/serializers.py

class UserSerializer(serializers.HyperlinkedModelSerializer):
profile = ProfileSerializer(required=True)
class Meta:
    model = User
    fields = ['id', 'url', 'first_name', 'last_name', 'email', 'password', 'profile']
    extra_kwargs = {'password': {'write_only': True}}

def create(self, validated_data):
    profile_data = validated_data.pop('profile')
    password = validated_data.pop('password')
    user = User(**validated_data)
    user.set_password(password)
    user.save()
    Profile.objects.create(user=user, **profile_data)
    return user

def update(self, instance, validated_data):
    profile_data = validated_data.pop('profile')
    profile = instance.profile
    instance.email = validated_data.get('email', instance.email)
    instance.first_name = validated_data.get('first_name', instance.first_name)
    instance.last_name = validated_data.get('last_name', instance.last_name)
    instance.save()

    profile.gender = profile_data.get('gender', profile.gender)
    profile.date_of_birth = profile_data.get('date_of_birth', profile.date_of_birth)
    profile.hometown = profile_data.get('hometown', profile.hometown)
    profile.phone = profile_data.get('phone', profile.phone)
    profile.addresses = profile_data.get('addresses', profile.addresses)
    profile.save()

It is working very well with user and profile I can create and update but when I add addresses it gives me error above shown in the title. I do not know yet how to solve nested inside nested relationships. Any help would be appreciated) Thanks beforehand!

Coexecutor answered 8/8, 2019 at 6:49 Comment(0)
H
9

Your issue is with this line

profile.addresses = profile_data.get('addresses', profile.addresses)

As the error message suggests, direct assignment to the reverse side of a relation is not allowed. What you want to do is use set() instead.

profile.addresses.set([list of new addresses])

https://docs.djangoproject.com/en/dev/ref/models/relations/#django.db.models.fields.related.RelatedManager.set

Hagar answered 8/8, 2019 at 6:58 Comment(1)
@jmp thanks for the answer but I do not know what to write. can you tell me please? If i write instances of Address model it says Address model got 'country' attributeCoexecutor
A
0

I had the same issue and this is how I fixed it.

Add an update method in your ProfileSerializer

def update(self, instance, validated_data):
   address_data = validated_data.pop('address')
   instance.gender = validated_data.get('gender', instance.gender)
   instance.date_of_birth = validated_data.get('date_of_birth', instance.date_of_birth)
   instance.hometown = validated_data.get('hometown', instance.hometown)
   instance.phone = validated_data.get('phone', instance.phone)
   
   for address_d in address_data:
      Address.objects.update(profile=instance, **address_data)

   instance.save()
   return instance

The update method of your UserSerializer should be:

def update(self, instance, validated_data):
 profile_data = validated_data.pop('profile')
 instance.email = validated_data.get('email', instance.email)
 instance.first_name = validated_data.get('first_name', instance.first_name)
 instance.last_name = validated_data.get('last_name', instance.last_name)
 
 for profile_d in profile_data:
     Profile.objects.update(user=instance, **profile_data)

 instance.save()
 return instance

Based on your code, you're trying to update the address directly when updating your user yet there's no direct relation. Updating the user updates the profile. Updating the profile updates the address.

Angy answered 25/1 at 5:43 Comment(0)
J
-2

romoving this line may solve your problem:

profile.addresses = profile_data.get('addresses', profile.addresses)
Jacobsohn answered 31/3, 2022 at 9:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.