Search code examples
djangodjango-rest-frameworkdjango-viewsdjango-serializer

How to update a User and its Profile in nested serializer using generic ListCreateAPIView?


I am working on genericAPIViews in DRF. I am using a built in user model with UserProfile model having one to one relation with it. But I am unable to update user due to nested serializer. My question is that how I can update my built in User model and Profile User model at the same time as UserProfile model is nested in User model.Here is my code:

Models.py

USER_CHOICE = (
    ('SS', 'SS'),
    ('SP', 'SP')
)
LOGIN_TYPE = (
    ('Local', 'Local'),
    ('Facebook', 'Facebook'),
    ('Google', 'Google')
)


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    cell_phone = models.CharField(max_length=15, blank=True, default="", null=True)
    country = models.CharField(max_length=50, blank=True, default="", null=True)
    state = models.CharField(max_length=50, blank=True, default="", null=True)
    profile_image = models.FileField(upload_to='user_images/', default='', blank=True)
    postal_code = models.CharField(max_length=50, blank=True, default="", null=True)
    registration_id = models.CharField(max_length=200, null=True, blank=True, default=None)
    active = models.BooleanField(default=True)
    # roles = models.ForeignKey(Role, null=True, on_delete=models.CASCADE, related_name='role', blank=True)
    user_type = models.CharField(max_length=50, choices=USER_CHOICE, null=True, blank=True)
    login_type = models.CharField(max_length=40, choices=LOGIN_TYPE, default='local')
    reset_pass = models.BooleanField(default=False)
    confirmed_email = models.BooleanField(default=False)
    remember_me = models.BooleanField(default=False)
    reset_code = models.CharField(max_length=200, null=True, blank=True, default="")
    reset_code_time = models.DateTimeField(auto_now_add=True, blank=True)
    longitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
    latitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
    r_code = models.CharField(max_length=15, null=True, blank=True)
    refer_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_refer")
    referred = models.ManyToManyField(User, related_name="user_referred", null=True, blank=True)
    otp = models.CharField(max_length=6, blank=True, default="", null=True)

    def __str__(self):
        return self.user.username

Seralizer.py

from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile


class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = '__all__'


class UserSerializer(serializers.ModelSerializer):
    profile = UserProfileSerializer()

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
    
    def create(self, validated_data):
       profile_data = validated_data.pop('profile')
       user = User.objects.create(**validated_data)
       Profile.objects.create(user=user, **profile_data)
       return user

Views.py

class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer



class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]

How can write my .update() methods which can be override according to DRF documentation.Thanks in advance for your addition to my knowledge.


Solution

  • There is update method in ModelSerializer which can be overriden the same way as you did with create.

    def update(self, instance, validated_data):
       profile_data = validated_data.pop('profile', {})
       profile = instance.profile
       for attr, value in profile_data.items():
           setattr(profile, attr, value)
       profile.save()
       return super(UserSerializer, self).update(instance, validated_data)
    

    You can also write nested serializer.