I was recently building a website for one of my clients by using the Django rest + React framework ( which is beautiful by the way ) and encountered a problem.
The Problem
If any of you have worked with Django Rest you'll know the multiple issues that pop up while making a custom user model, and specifically how the changes you make don't reflect upon a save.
I'll show you the best way (in my opinion) to build a custom user model in DRF (Django Rest Framework), without a username.
Step 1
Create an app called user ( Or whatever you want to call it ) and in the models.py make a User model which extends AbstractBaseUser
from django.db import models
from django.contrib.auth.models import AbstractBaseUser,BaseUserManager
import datetime
class User(AbstractBaseUser):
username = None
email = models.EmailField(_('email address'), unique=True)
name = models.CharField(max_length=100)
date_of_birth = models.DateField(default=datetime.date.today)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [ 'date_of_birth','name' ]
def __str__(self): # __unicode__ on Python 2
return self.email
Step 2
And now we add a very important line in the model
objects = UserManager()
This basically tells Django how it's supposed to store the Object and with what attributes depending on permissions.
Step 3
We add the usermanager
class UserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, email, name, date_of_birth, password=None):
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
name=name,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, email, name, date_of_birth, password):
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
name=name,
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, email, name, date_of_birth, password):
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
name= "True",
)
user.staff = True
user.admin = True
user.save(using=self._db)
return user
Step 4
Build the CustomRegisterSerializer
from rest_auth.registration.serializers import RegisterSerializer
class CustomRegisterSerializer(RegisterSerializer):
email = serializers.EmailField(required=True)
password1 = serializers.CharField(write_only=True)
name = serializers.CharField(required=True)
date_of_birth = serializers.DateField(required=True)
def get_cleaned_data(self):
super(CustomRegisterSerializer, self).get_cleaned_data()
return {
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', ''),
'name': self.validated_data.get('name', ''),
'date_of_birth': self.validated_data.get('date_of_birth', ''),
}
class CustomUserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email','name','date_of_birth')
read_only_fields = ('email',)
Create the custom view which just has all user objects as a query set
from rest_auth.registration.views import RegisterView
class CustomRegisterView(RegisterView):
queryset = User.objects.all()
Finally update the settings.py to apply these
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_EMAIL_FIELD = 'email'
ACCOUNT_LOGOUT_ON_GET = True
AUTH_USER_MODEL = 'users.User'
REST_AUTH_SERIALIZERS = {
"USER_DETAILS_SERIALIZER": "users.serializers.CustomUserDetailsSerializer",
}
REST_AUTH_REGISTER_SERIALIZERS = {
"REGISTER_SERIALIZER": "users.serializers.CustomRegisterSerializer",
}
And just add the view to the URL and you're good to go!
I honestly believe that the Django Rest + React stack is one of the best ones in the game right now, but the Django Rest Framework does need a little bit more work to make it perfect for most developers.
Hit me up if you need help! Or have some projects that need people to work with :D
Top comments (17)
it is patially working. It didn't pass any values of new feilds. check this plzzz
stackoverflow.com/questions/615595...
1) I have an existing user table called user_master. It has more than 10000 existing records which was moved from the legacy system. Instead of using django's defult user table, can I use this table?
2)Second question is can I avoid using django model for managing users. means I will insert the users directly by calling Postgresql function. I want the django login check using the same user_master table.
Thanks for this nice tutorial! Can you provide some info on how to update fields on the registration serializer (CustomRegisterSerializer) so it won't display the inherited fields from the parent class in the browsable API.
great post Tarush! would you make a post explaining how to do the same but with having the user profile as a separate model?
It is currently a separate model
I'm sorry but I just see one model here:
class User(AbstractBaseUser)
Hi Tarush,
Hi! There are multiple ways to do so, In the scenario for this post I had deployed my django app as a REST API and then made POST/GET Requests from my react app to django, It's makes communications very simple and straight forward.
Have you used nginx to integrate fronend and backend
I think you're a little confused, nginx is a web server which mostly helps us while we're hosting it somewhere. To "integrate" frontend and backend you just need to make POST/GO requests from the frontend to the backend
hi Tarush i need your help with one of my Drf react project i just wanted to tweak a few things but i am not able to do that because i am a newbie to Django rest framework
Hey! Drop me a mail
Thanks for the explanation.
can you tell me how can I add an image filed in it.
I need to upload user's profile picture
Just check out the Django documentation, they have a great example and explanation.
docs.djangoproject.com/en/2.2/
You should make it clear, that you will use which library for a faster reading =)
Not bad post anyway!
Hi Tarush,
Would you kindly assist in creating a login Serializer ,model and View for users to login with token authentication? But Custom based Models. I'm a bit stuck somewhere
Great Article. Thanks for sharing.
Instead of username being email address what if you wanted to use phone number ?
What other changes would one need to make ? Thanks