Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend user model Django REST framework 3.x.x

I am trying to extend the Django rest framework (version 3.x.x) with gender, created_at, updated_at fields that are defined in a separate model called UserProfile. When I try to update the UserProfile model instance including the nested User model instance, it updates only the UserProfile instance (gender, updated_at fields). So basically what I want to achieve is to be able to update also the email, first_name and last_name fields from the User model

models.py:

class UserProfile(models.Model):
        user = models.OneToOneField(User, primary_key = True, related_name = 'profile')
        gender = models.CharField(choices = GENDERS, default = 2, max_length = 64)
        created_at = models.DateTimeField(auto_now_add = True)
        updated_at = models.DateTimeField(auto_now = True)

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

        @receiver(post_save, sender = User)
        def create_profile_for_user(sender, instance = None, created = False, **kwargs):
                if created:
                        UserProfile.objects.get_or_create(user = instance)

        @receiver(pre_delete, sender = User)
        def delete_profile_for_user(sender, instance = None, **kwargs):
                if instance:
                        user_profile = UserProfile.objects.get(user = instance)
                        user_profile.delete()

serializers.py:

class UserProfileSerializer(serializers.ModelSerializer):

        id = serializers.IntegerField(source = 'pk', read_only = True)
        username = serializers.CharField(source = 'user.username', read_only = True)
        email = serializers.CharField(source = 'user.email')
        first_name = serializers.CharField(source = 'user.first_name')
        last_name = serializers.CharField(source = 'user.last_name')

        class Meta:
                model = UserProfile
                fields = (
                        'id', 'username', 'email', 'first_name', 'last_name',
                        'created_at', 'updated_at', 'gender',
                )
                read_only_fields = ('created_at', 'updated_at',)

        def update(self, instance, validated_data):
                #user = User.objects.get(pk = instance.user.pk);        
                user = instance.user
                user.email = validated_data.get('user.email', user.email)
                user.first_name = validated_data.get('user.first_name', user.first_name)
                user.last_name = validated_data.get('user.last_name', user.last_name)
                user.save()

                instance.gender = validated_data.get('gender', instance.gender)
                instance.save()

                return instance

        def create(self, validated_data):

                user_data = validated_data.pop('user')
                user = User.objects.create(**user_data)

                profile = UserProfile.objects.create(user = user, **validated_data)
                return profile

views.py:

class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserSerializer

class UserProfileViewSet(viewsets.ModelViewSet):
        queryset = UserProfile.objects.all()
        serializer_class = UserProfileSerializer
like image 886
Adam Avatar asked Apr 05 '15 13:04

Adam


People also ask

How do I update user details in Django REST Framework?

Open auth/urls.py and add update profile endpoint. we should send a PUT request to API for checking update profile endpoint. We must add username, first_name, last_name and email. If fields passed validations, user profile will be changed.

What is serializing in Django?

Django's serialization framework provides a mechanism for “translating” Django models into other formats. Usually these other formats will be text-based and used for sending Django data over a wire, but it's possible for a serializer to handle any format (text-based or not).

What is Django Restapi?

Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.


2 Answers

A slight modification to the above code replacing the None with {}

Because 'NoneType' object has no attribute 'items'

def update(self, instance, validated_data):
    # First, update the User
    user_data = validated_data.pop('user', {})
    for attr, value in user_data.items():
        setattr(instance.user, attr, value)
    # Then, update UserProfile
    for attr, value in validated_data.items():
        setattr(instance, attr, value)
    instance.save()
    return instance

There is another way that I am implementing which takes care of validation. I am implementing it for handling PATCH

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

class ProfileSerializer(serializers.ModelSerializer):
    email = serializers.CharField(source='user.email')
    first_name = serializers.CharField(source='user.first_name')
    last_name = serializers.CharField(source='user.last_name')

    class Meta:
        model = Profile
        exclude = ('user',)

    def update(self, instance, validated_data):
        user_data = validated_data.pop('user', {})
        user_serializer = UserSerializer(instance.user, data=user_data, partial=True)
        user_serializer.is_valid(raise_exception=True)
        user_serializer.update(instance.user, user_data)
        super(ProfileSerializer, self).update(instance, validated_data)
        return instance
like image 148
Prabin Kumar Sahu Avatar answered Sep 26 '22 22:09

Prabin Kumar Sahu


Here's how I did it:

def update(self, instance, validated_data):
    # First, update the User
    user_data = validated_data.pop('user', None)
    for attr, value in user_data.items():
            setattr(instance.user, attr, value)
    # Then, update UserProfile
    for attr, value in validated_data.items():
        setattr(instance, attr, value)
    instance.save()
    return instance

Basically, I looked into the original code for serializers (here, line 800) and modified it so that it would fit my needs.

Good luck.

like image 45
AdelaN Avatar answered Sep 24 '22 22:09

AdelaN