Search code examples
djangopython-3.xdjango-rest-frameworkauthorizationdjango-allauth

Django Rest Framework Custom Register View (RegisterView) failing to return token + user data


I'm using allauth and rest_auth in my django rest framework app. When registering an user a token (key) is returned, what I'm trying to achieve is to return the token + user data after registration. To do so I've the following serializer:

from rest_framework import serializers
from rest_framework.response import Response
from allauth.account import app_settings as allauth_settings
from allauth.utils import email_address_exists
from allauth.account.adapter import get_adapter
from allauth.account.utils import setup_user_email
from kofiapi.api.users.models import User, UserProfile
from rest_framework.authtoken.models import Token
from rest_auth.models import TokenModel

class UserProfileSerializer(serializers.ModelSerializer):

    class Meta:
        model = UserProfile
        fields = ('dob', 'phone', 'receive_newsletter')


class UserSerializer(serializers.HyperlinkedModelSerializer):

    profile = UserProfileSerializer(required=True)

    class Meta:
        model = User
        fields = ('url', 
                  'email', 
                  'first_name', 
                  'last_name',
                  'password',
                  'profile')
        extra_kwargs = {'password': {'write_only': True}}

    def create_token(self, user):
        token, created = Token.objects.get_or_create(user=user)
        return token


    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()

        token = self.create_token(user)

        UserProfile.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.name)
        instance.save()

        profile.dob = profile_data.get('dob', profile.dob)
        profile.phone = profile_data.get('phone', profile.phone)
        profile.receive_newsletter = profile_data.get('receive_newsletter', profile.receive_newsletter)
        profile.save()

        return instance


class TokenSerializer(serializers.ModelSerializer):

    user = serializers.SerializerMethodField()

    class Meta:
        model = TokenModel
        fields = ('key', 'user')

    def get_user(self, instance):
        request = self.context.get('request')
        serializer_context = {
            'request': request,
        }
        return UserSerializer(request.user, context=serializer_context).data

and the following view:

from rest_framework.permissions import AllowAny
from kofiapi.api.permissions import IsLoggedInUser, IsAdminUser
from rest_framework.viewsets import ModelViewSet
from rest_auth.registration.views import RegisterView
from rest_framework import viewsets

from kofiapi.api.users.models import User
from kofiapi.api.users.serializers import UserSerializer

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

    def get_permissions(self):

        permission_classes = []
        if self.action == 'create':
            permission_classes = [AllowAny]
        elif self.action == 'retrieve' or self.action == 'update' or self.action == 'partial_update':
            permission_classes = [IsLoggedInUser]
        elif self.action == 'list' or self.action == 'destroy':
            permission_classes = [IsAdminUser]
        return [permission() for permission in permission_classes]

class CustomRegisterView(RegisterView):

    def get_response_data(self, user):

        if allauth_settings.EMAIL_VERIFICATION == allauth_settings.EmailVerificationMethod.MANDATORY:
            return {"detail": _("Verification Email Sent")}

        if getattr(settings, 'REST_USE_JWT', False):
            data = {
                'user': user,
                'token': self.token
            }
            return JWTSerializer(data).data
        else:
            return TokenSerializer(user.auth_token, context={"request": self.request}).data

I was getting an error after submission related to settings so I removed this part:

if getattr(settings, 'REST_USE_JWT', False):
                data = {
                    'user': user,
                    'token': self.token
                }
                return JWTSerializer(data).data

but upon submitting a new user for registration I'm getting now the following error:

AttributeError at /api/rest-auth/registration/
Got AttributeError when attempting to get a value for field `email` on serializer `UserSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `AnonymousUser` instance.
Original exception text was: 'AnonymousUser' object has no attribute 'email'.

Solution

  • If you look at the source code of rest-auth you will see that during registration, they don't put user in the request object anywhere, that's why when you try to fetch the user from request object in your TokenSerializer it is an AnyonymousUser.

    What you can do is to pass the user in the context along with the request in your custom view or set it in the request object like django does at the time of login.