Would like to allow users to update their profile, getting the error
Field name 'city' is not valid for model 'User'.
For context, I extended my default user class in models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
city = models.CharField(max_length=50,blank=True)
country = models.CharField(max_length=50, blank=True)
bio = models.CharField(max_length=500, blank=True)
profile_pic = models.ImageField(upload_to='profile/%Y/%m/%d', default='media/placeholder.png', blank=False, null=False)
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
When a user updates a profile I use this endpoint in urls.py:
path('update_profile/<int:pk>', views.UpdateProfileView.as_view(), name='update_profile'),
Here is my UpdateProfileView:
class UpdateProfileView(generics.UpdateAPIView):
queryset = User.objects.all()
serializer_class = UpdateUserSerializer
def profile(request):
if request.method == 'PUT':
try:
user = User.objects.get(id=request.user.id)
serializer_user = UpdateUserSerializer(user, many=True)
if serializer_user.is_valid():
serializer_user.save()
return Response(serializer_user)
except User.DoesNotExist:
return Response(data='no such user!', status=status.HTTP_400_BAD_REQUEST)
and my serializers.py:
class UpdateUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=False)
class Meta:
model = User
fields = ['username', 'email', 'password', 'first_name', 'last_name','city','country']
extra_kwargs = {'username': {'required': False},
'email': {'required': False},
'password': {'required': False},
'first_name': {'required': False},
'last_name': {'required': False},
'city': {'required': False},
'country': {'required': False}}
def validate_email(self, value):
user = self.context['request'].user
if User.objects.exclude(pk=user.pk).filter(email=value).exists():
raise serializers.ValidationError({"email": "This email is already in use."})
return value
def validate_username(self, value):
user = self.context['request'].user
if User.objects.exclude(pk=user.pk).filter(username=value).exists():
raise serializers.ValidationError({"username": "This username is already in use."})
return value
def update(self, instance, validated_data):
#re-writing updated profile info from request
user = self.context['request'].user
if user.pk != instance.pk:
raise serializers.ValidationError({"authorize": "You don't have permission for this user."})
instance.first_name = validated_data['first_name']
instance.last_name = validated_data['last_name']
instance.email = validated_data['email']
instance.username = validated_data['username']
instance.profile.city = validated_data['city']
instance.profile.country = validated_data['country']
instance.profile.bio = validated_data['bio']
instance.save()
return instance
Please let me know where I am going wrong
CodePudding user response:
In this part of the serializer:
fields = ['username', 'email', 'password', 'first_name', 'last_name','city','country']
'city' is not an attribute of User, it's an attribute of Profile. In order for DRF to update it, it needs to know how to access it.
Example:
class UpdateUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=False)
city = serializers.CharField(source='profile.city')
[...]
CodePudding user response:
like @Nick ODell said
"'city' is not an attribute of User, it's an attribute of Profile. In order for DRF to update it, it needs to know how to access it."
but couldn't we just change the model the serializer class is using like
class UpdateUserSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=False)
class Meta:
model = Profile
fields = ['username', 'email', 'password', 'first_name', 'last_name','city','country']
extra_kwargs = {'username': {'required': False},
'email': {'required': False},
'password': {'required': False},
'first_name': {'required': False},
'last_name': {'required': False},
'city': {'required': False},
'country': {'required': False}}