Django rest-auth allauth registration with email, first and last name, and without username
Asked Answered
A

4

38

I am using django-rest-auth and allauth for login and registration in my django app. I haven't written any extra single line of code of my own for login or registration. Registration is successful with emailid and provided password.

I am not using username for authentication, instead email.

In my browsable api for registration I get following:

enter image description here

Along with these fields I want to have first_name and last_name (the default auth_user table had these columns) so that my newly created auth_user also has these fields set along with email and hashed password.

How can I achieve this? This browsable form itself is not so important but being able to store first_name and last_name is what I need primarily.

Anthraquinone answered 28/4, 2016 at 9:21 Comment(1)
Googling for the same thing saw a similarity in the avatars here: github.com/pennersr/django-allauth/issues/1412 . Had to mention it apologies :)Thurgau
M
42
  1. Make sure you have ACCOUNT_USERNAME_REQUIRED = False in your settings.py file.

  2. For first_name and last_name you need to write a custom RegisterSerializer (https://github.com/iMerica/dj-rest-auth/blob/bf168d9830ca2e6fde56f83f46fe48ab0adc8877/dj_rest_auth/registration/serializers.py#L197)

here's a sample code for serializers.py

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

class RegisterSerializer(serializers.Serializer):
    first_name = serializers.CharField(required=True, write_only=True)
    last_name = serializers.CharField(required=True, write_only=True)
    email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
    password1 = serializers.CharField(write_only=True)
    password2 = serializers.CharField(write_only=True)

    def validate_username(self, username):
        username = get_adapter().clean_username(username)
        return username

    def validate_email(self, email):
        email = get_adapter().clean_email(email)
        if allauth_settings.UNIQUE_EMAIL:
            if email and email_address_exists(email):
                raise serializers.ValidationError(
                    _('A user is already registered with this e-mail address.'),
                )
        return email

    def validate_password1(self, password):
        return get_adapter().clean_password(password)

    def validate(self, data):
        if data['password1'] != data['password2']:
            raise serializers.ValidationError(_("The two password fields didn't match."))
        return data

    def custom_signup(self, request, user):
        pass

    def get_cleaned_data(self):
        return {
            'first_name': self.validated_data.get('first_name', ''),
            'last_name': self.validated_data.get('last_name', ''),
            'username': self.validated_data.get('username', ''),
            'password1': self.validated_data.get('password1', ''),
            'email': self.validated_data.get('email', ''),
        }

    def save(self, request):
        adapter = get_adapter()
        user = adapter.new_user(request)
        self.cleaned_data = self.get_cleaned_data()
        user = adapter.save_user(request, user, self, commit=False)
        if "password1" in self.cleaned_data:
            try:
                adapter.clean_password(self.cleaned_data['password1'], user=user)
            except DjangoValidationError as exc:
                raise serializers.ValidationError(
                    detail=serializers.as_serializer_error(exc)
            )
        user.save()
        self.custom_signup(request, user)
        setup_user_email(request, user, [])
        return user
  1. In settings.py make sure you add to refer to new Serializer.

    REST_AUTH_REGISTER_SERIALIZERS = {
            'REGISTER_SERIALIZER': 'path.to.RegisterSerializer',
    }
    
Marvismarwin answered 28/4, 2016 at 10:4 Comment(3)
Is that all? No changes in views?Anthraquinone
this works perfect, user is saved with first and last name! However I get an error saying 'User' object has no attribute 'profile' at user.profile.save() this line..Anthraquinone
Solved! I changed user.profile.save() to user.save() Thanks a lot!Anthraquinone
S
28

You can also just overwrite the custom_signup method on RegisterSerializer, which is intended for this purpose.

from rest_auth.registration.serializers import RegisterSerializer
from rest_auth.registration.views import RegisterView
from rest_framework import serializers


class NameRegistrationSerializer(RegisterSerializer):

  first_name = serializers.CharField(required=False)
  last_name = serializers.CharField(required=False)

  def custom_signup(self, request, user):
    user.first_name = self.validated_data.get('first_name', '')
    user.last_name = self.validated_data.get('last_name', '')
    user.save(update_fields=['first_name', 'last_name'])


class NameRegistrationView(RegisterView):
  serializer_class = NameRegistrationSerializer

Then use the following in your urls.py

url(r'^rest-auth/registration/name-registration/$', NameRegistrationView.as_view(), name="rest_name_register")

or set REGISTER_SERIALIZER in settings.py

REST_AUTH_REGISTER_SERIALIZERS = {
    'REGISTER_SERIALIZER': 'path.to.RegisterSerializer',
}
Stakeout answered 7/8, 2017 at 10:3 Comment(3)
This is a better way to do it. Just a suggestion: You don't need a view if you just want to override the default register view.Dundee
Damn you saved my day buddyDemythologize
This is a much better way to do it than the accepted answer.Vyborg
A
16

A more elegant solution would be to inherit from RegisterSerializer and extend as needed.

class MyRegisterSerializer(RegisterSerializer):
    first_name = serializers.CharField(required=True, write_only=True)
    last_name = serializers.CharField(required=True, write_only=True)

    def get_cleaned_data(self):
        return {
            'first_name': self.validated_data.get('first_name', ''),
            'last_name': self.validated_data.get('last_name', ''),
            'password1': self.validated_data.get('password1', ''),
            'email': self.validated_data.get('email', ''),
        }

    def save(self, request):
        adapter = get_adapter()
        user = adapter.new_user(request)
        self.cleaned_data = self.get_cleaned_data()
        adapter.save_user(request, user, self)
        setup_user_email(request, user, [])
        user.save()
        return user
Addy answered 22/6, 2017 at 3:12 Comment(4)
Works like a charm with the minimum amount of custom code. I think this is the more elegant solution. Thanks for posting.Semester
Works great, but I think the save function is not necessary, right?Embargo
Yeah, I also agree to the above comment.Spoonful
I can confirm the save function is not needed indeed, but perhaps you need a custom adapter. This answer explains a decent step-by-step.Saunders
U
0

For anyone who needs a quick solution

from dj_rest_auth.registration.serializers import RegisterSerializer
from rest_framework import serializers


class CustomRegistration(RegisterSerializer):
     first_name = serializers.CharField(write_only=True)
     last_name = serializers.CharField(write_only=True)
     def custom_signup(self, request, user):
         first = request.POST.get("first_name")
         last = request.POST.get("last_name")
         user.first_name = first
         user.last_name = last
         user.save()

`

Be Sure to include path to custom registration in settings:

REST_AUTH_REGISTER_SERIALIZERS = {
      'REGISTER_SERIALIZER': 'path_to.CustomRegistration',
}
Ungodly answered 10/12, 2022 at 15:1 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.