So I was trying to build a backend for my Android application. I was trying to apply a login procedure that works with JWT Token
This is what I have done :
I have made a custom User model.
Customize my superuser to take phone number and password, instead of username and password
I have successfully created superuser and store it in my database (using postgreSQL).
I have also customize my token claims and response as seen in my serializers.py of class LoginSerializer.
However, I encounter some problem after what I did :
Right now, after customizing my user model, I was not able to login to Django administration with my new custom made User model, even though I have succesfully created a superuser.
I still can't get the token even after I have made a customization for my token claims, with the success superuser account I just created.
Here are some of the error message :
Here are the some of the files attatched below :
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser,BaseUserManager
from django.utils.translation import ugettext_lazy as _
from phone_field import PhoneField
class RegisterUserManager(BaseUserManager):
def create_user(self, phone_number,password):
if not phone_number:
raise ValueError('The phone number must be set')
user = self.model(
phone_number=phone_number,
password = password,)
user.save(using = self._db)
return user
def create_superuser(self,phone_number,password, **extra_fields):
user = self.create_user(
phone_number,
password = password
)
user.is_admin = True
user.save(using= self._db)
return user
class RegisterUser(AbstractBaseUser):
first_name = models.CharField(name = 'first_name',max_length=255,default = '')
last_name = models.CharField(name='last_name', max_length=255,default = '')
email = models.EmailField(name='email', max_length = 255)
phone_number = PhoneField(name='phone_number',unique=True)
birthday = models.DateField(name ='birthday',null= True)
nickname = models.CharField(max_length=100,name = 'nickname')
is_active = models.BooleanField(default = True)
is_admin = models.BooleanField(default= False)
last_login = models.DateTimeField(auto_now= True)
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS = []
objects = RegisterUserManager()
def __str__(self):
return self.phone_number
def has_perm(self, perm, obj = None):
return True
def has_module_perms(self,perm,obj = None):
return True
#property
def is_staff(self):
return self.is_admin
views.py
from django.shortcuts import render
from django.http import HttpResponse,JsonResponse
from rest_framework.parsers import JSONParser
from restaccount.models import RegisterUser
# Login
from restaccount.serializers import RegisterSerializers,LoginSerializer
# LoginSerializers
from django.views.decorators.csrf import csrf_exempt
from rest_framework.generics import CreateAPIView
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework.permissions import (AllowAny,IsAuthenticated)
# from rest_framework.generics import CreateAPIView
class RegisterView(CreateAPIView):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializers
queryset = RegisterUser.objects.all()
class LoginView(TokenObtainPairView):
serializer_class = LoginSerializer
serializers.py
from rest_framework.serializers import (ModelSerializer,ValidationError)
from restaccount.models import RegisterUser
# Login
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import serializers
class RegisterSerializers(ModelSerializer):
class Meta:
model = RegisterUser
fields =['id',
'first_name',
'last_name',
'email',
'password',
'phone_number',
'nickname',
'birthday',
]
def create(self,validated_data):
first_name = validated_data['first_name']
last_name = validated_data['last_name']
email = validated_data['email']
password = validated_data['password']
phone_number = validated_data['phone_number']
nickname = validated_data['nickname']
birthday = validated_data['birthday']
user_obj = RegisterUser(
first_name = first_name,
last_name = last_name,
email = email,
password = password,
phone_number = phone_number,
nickname = nickname,
birthday = birthday,
)
user_obj.save()
return user_obj
def update(self, instance,validated_data):
instance.first_name = validated_data.get('first_name',instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.email = validated_data.get('email', instance.email)
instance.password = validated_data.get('password', instance.password)
instance.phone_number = validated_data.get('phone_number', instance.phone_number)
instance.nickname = validated_data.get('nickname', instance.nicknames)
instance.birthday = validated_data.get('birthday',instance.birthday)
instance.save()
return instance
def validate(self,data):
return data
def validate_phone_number(self,value):
phone_number = value
user_qs = RegisterUser.objects.filter(phone_number = phone_number)
if user_qs.exists():
raise ValidationError("This phone number is registered")
return value
class LoginSerializer(TokenObtainPairSerializer):
#classmethod
def get_token(cls,user):
token = super().get_token(user)
token['phone_number'] = user.phone_number
token['password'] = user.password
return token
def validate(self,attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
data['refresh'] = str(refresh)
data['access'] = str(refresh.access_token)
data['phone_number'] = self.user.phone_number
return data
Turns out that the password field in the model have to be hashed such that
user.set_password(password). This is also the same case, if you want to create a user from the API endpoint. You have to store the hashed password in your database.
However, I don't know why this is the behaviour.
Related
from django.db import models
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin)
from rest_framework_simplejwt.tokens import RefreshToken
class UserManager(BaseUserManager):
def create_user(self,username,email, password=None ):
if username is None:
raise TypeError("Users should have a username")
if email is None:
raise TypeError("Users should have an Email")
user =self.model(username=username, email=self.normalize_email(email))
user.set_password(password)
user.save()
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError("Password should not be none")
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=100, unique=True, db_index=True)
email = models.EmailField(max_length=100, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username"]
objects = UserManager()
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return {
"refresh":str(refresh),
"access": str(refresh.access_token)
}
Below is the serializers.py file
from .models import User
from rest_framework import serializers
from django.contrib import auth
from rest_framework.exceptions import AuthenticationFailed
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=50, min_length=6, write_only =True)
class Meta:
model = User
fields = ["email", "username", "password"]
def validate(self, attrs):
email = attrs.get("email", '')
username = attrs.get("username", '')
if not username.isalnum():
raise serializers.ValidationError("The username should contain only alphanumeric characters")
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class EmailVerificationSerializer(serializers.ModelSerializer):
token = serializers.CharField(max_length=555)
class Meta:
model = User
fields = ["token"]
class LoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(max_length=255,min_length=3)
password = serializers.CharField(max_length=68, min_length=6, write_only=True)
username = serializers.CharField(max_length=255,min_length=3, read_only=True)
tokens = serializers.CharField(max_length=68, min_length=6,read_only=True)
class Meta:
model = User
fields = ["email", "password","username","tokens"]
def validate(self,attrs):
email = attrs.get("email", "")
password = attrs.get("password", "")
user = auth.authenticate(email=email, password=password)
if not user:
raise AuthenticationFailed("Invalid Credentials, try again")
if not user.is_active:
raise AuthenticationFailed("Account disabled, contact admin")
if not user.is_verified:
raise AuthenticationFailed("Email is not verified")
return super().validate(attrs,{
"email":user.email,
"username": user.username,
"tokens":user.tokens
})
Below is the views.py file
**from django.shortcuts import render
from rest_framework import generics, status, views
from .serializers import EmailVerificationSerializer, RegisterSerializer, LoginSerializer
from rest_framework.response import Response
from rest_framework_simplejwt.tokens import RefreshToken, AccessToken
from .models import User
from .utils import Util
from django.contrib.sites.shortcuts import get_current_site
from django.urls import reverse
from django.conf import settings
from drf_yasg.utils import swagger_auto_schema
from drf_yasg import openapi
import jwt**
# Create your views here.
class RegisterView(generics.GenericAPIView):
serializer_class = RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data= serializer.data
user = User.objects.get(email=user_data["email"])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
relativeLink = reverse("email-verify")
absurl= "http://" + current_site + relativeLink + "?token="+ str(token)
email_body ="Hi "+ user.username + " Use link below to verify your email \n" + absurl
data = {"email_body":email_body,"to_email":user.email, "email_subject":"Verify your email"}
Util.send_email(data)
return Response(user_data, status=status.HTTP_201_CREATED)
class VerifyEmail(views.APIView):
serializer_class = EmailVerificationSerializer
token_param_config = openapi.Parameter(
"token", in_=openapi.IN_QUERY, description="Description",type=openapi.TYPE_STRING)
#swagger_auto_schema(manual_parameters=[token_param_config])
def get(self,request):
token = request.GET.get("token")
try:
payload = jwt.decode(token, settings.SECRET_KEY)
user = User.objects.get(id=payload["user_id"])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({"email":"Successfully activated"}, status=status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({"error": "Activation Link Expired"}, status=status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({"error": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)
class LoginAPIView(generics.GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
Below is the utils.py for sending a mail
from django.core.mail import EmailMessage
class Util:
#staticmethod #We'll use the class method without instantiating it
def send_email(data):
email =EmailMessage(
subject=data["email_subject"], body=data["email_body"], to=[data["to_email"]])
email.send()
Below is the urls.py
from django.urls import path
from .views import RegisterView, VerifyEmail, LoginAPIView
urlpatterns = [
path("register/", RegisterView.as_view(), name="register"),
path("email-verify/", VerifyEmail.as_view(), name="email-verify"),
path("login/", LoginAPIView.as_view(), name="login")
]
Below is the verification mail sent
A verification mail sent
Below are the error gotten while trying to verify the token
A screenshot image description
at VerifyEmailView class the payload needs the algorithm with which you decode your token.`class VerifyEmail(views.APIView):
serializer_class = EmailVerificationSerializer
token_param_config = openapi.Parameter(
"token", in_=openapi.IN_QUERY, description="Description",type=openapi.TYPE_STRING)
#swagger_auto_schema(manual_parameters=[token_param_config])
def get(self,request):
token = request.GET.get("token")
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms='HS256')
user = User.objects.get(id=payload["user_id"])
if not user.is_verified:
user.is_verified = True
user.save()
return Response({"email":"Successfully activated"}, status=status.HTTP_200_OK)
except jwt.ExpiredSignatureError as identifier:
return Response({"error": "Activation Link Expired"}, status=status.HTTP_400_BAD_REQUEST)
except jwt.exceptions.DecodeError as identifier:
return Response({"error": "Invalid token"}, status=status.HTTP_400_BAD_REQUEST)`
I'm trying to solve some of the things that don't fit me in the current django rest auth.
First of all, I would like to make it possible to sign up as a member only once I enter the password without using password2 in the rest auth.
And when I sign up as a member, the following error occurs when I insert the duplicate email.
UNIQUE constraint failed: api_user.email
How can I solve these problems? Here is my code.
models.py
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.utils.translation import ugettext_lazy as _
class UserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, email, profile, userName, password):
user = self.model(
email=self.normalize_email(email),
userName=userName,
profile=profile,
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, userName, profile):
user = self.create_user(
email=self.normalize_email(email),
password=password,
userName=userName,
profile=profile,
)
user.is_staff = True
user.is_superuser = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
username = None
email = models.EmailField(_('email address'), unique=True)
userName = models.CharField(max_length=10)
profile = models.ImageField(default='default_image.jpeg')
is_staff = models.BooleanField(_('staff status'), default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['userName', 'profile']
serializers.py
from .models import User
from rest_framework import serializers
from rest_auth.registration.serializers import RegisterSerializer
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenRefreshSerializer
from rest_framework_simplejwt.tokens import RefreshToken
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 django.utils.translation import ugettext_lazy as _
from datetime import datetime, timedelta
class customRegisterSerializer(RegisterSerializer):
username = None
userName = serializers.CharField(required=True)
profile = serializers.ImageField(use_url=True)
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_password(self, password):
return get_adapter().clean_password(password)
def get_cleaned_data(self):
return {
'email': self.validated_data.get('email', ''),
'password': self.validated_data.get('password', ''),
'userName': self.validated_data.get('userName', ''),
'profile': self.validated_data.get('profile', ''),
}
def save(self, request, **kwargs) :
adapter = get_adapter()
user = adapter.new_user(request)
user.save()
return user
class customTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
data = super().validate(attrs)
refresh = self.get_token(self.user)
del(data['refresh'])
del(data['access'])
data['token_type'] = 'bearer'
data['access_token'] = str(refresh.access_token)
data['expires_at'] = str(datetime.now() + timedelta(hours=6))
data['refresh_token'] = str(refresh)
data['refresh_token_expires_at'] = str(datetime.now() + timedelta(days=30))
return data
class customTokenRefreshSerializer (TokenRefreshSerializer) :
def validate(self, attrs):
data = super().validate(attrs)
refresh = RefreshToken(attrs['refresh'])
del(data['refresh'])
data['token_type'] = 'bearer'
data['access_token'] = str(refresh.access_token)
data['expires_at'] = str(datetime.now() + timedelta(hours=6))
data['refresh_token'] = str(refresh)
data['refresh_token_expires_at'] = str(datetime.now() + timedelta(days=30))
return data
thanks in advance
I want to implement update User Profile,in django rest framework.
I am getting the above mentioned error, no matter what I try and change in my code.
Below is the the code for my user serializers, user model and api views.
users/api/serializers.py:
from rest_framework import serializers
from ..models import User,Profile
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username','first_name','last_name','phone','id')
class UserProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = Profile
fields = ('user', 'bio', 'image')
def update(self,instance,validated_data):
user_data = validated_data.pop('user',{})
user = instance.user
instance.bio = validated_data.get('bio',instance.bio)
instance.image = validated_data.get('image',instance.image)
instance.save()
return instance
Is there anything wrong in my serializer, cause I tried hardcoding and saving a particular user profile also by using for ex: instance.user.id = 21 and then saving it, but i get the same error
Is there anything wrong in my serializer, cause I tried hardcoding and saving a particular user profile also by using for ex: instance.user.id = 21 and then saving it, but i get the same error
users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.conf import settings
import os
import random
import string
class MyAccountManager(BaseUserManager):
def create_user(self,phone,username,first_name,last_name):
if not phone:
raise ValueError("Users must have a valid phone number")
if not username:
raise ValueError("Users must have a valid username")
if not first_name:
raise ValueError("Users must have a valid First Name")
if not last_name:
raise ValueError("Users must have a valid last name")
user = self.model(
phone=phone,
username=username,
first_name=first_name,
last_name=last_name
)
user.save(using=self._db)
return user
def create_superuser(self,username,phone,first_name,last_name,password = None):
user = self.create_user(
username=username,
phone=phone,
first_name=first_name,
last_name=last_name,
)
user.set_password(password)
user.is_admin=True
user.is_staff=True
user.is_superuser=True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
phone = models.CharField(unique=True,max_length=20)
username = models.CharField(max_length=50,unique=True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=30, blank=True)
date_joined = models.DateTimeField(verbose_name='date_joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last_login', auto_now_add=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
otp = models.CharField(max_length=10,default='')
USERNAME_FIELD = 'phone'
REQUIRED_FIELDS = ['username','first_name','last_name']
objects = MyAccountManager()
def __str__(self):
return self.username
def has_perm(self,perm,obj = None):
return self.is_admin
def has_module_perms(self,app_label):
return True
class Profile(models.Model):
user = models.OneToOneField(User,on_delete = models.CASCADE)
bio = models.CharField(max_length=140)
image = models.ImageField(default='default.jpg',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username}'
users/api/views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework import viewsets
from rest_framework.views import APIView
from .serializers import UserSerializer,UserProfileSerializer
from ..models import User,Profile
from django_filters.rest_framework import DjangoFilterBackend
class ProfileViewset(viewsets.ModelViewSet):
model = Profile
serializer_class = UserProfileSerializer
queryset = Profile.objects.all()
filter_backends = [DjangoFilterBackend]
filterset_fields = ['user']
Please add null=True if you want to insert null value.This is my suggestion
class Profile(models.Model):
user = models.OneToOneField(User,null=True,on_delete = models.CASCADE)
bio = models.CharField(max_length=140)
image = models.ImageField(default='default.jpg',upload_to='profile_pics')
def __str__(self):
return f'{self.user.username}'
I am implementing user authentication with django-rest_framework_simple-jwt with custom user,
My models.py:
class UserManager(BaseUserManager):
def create_user(self, email, username, password, alias=None):
user = self.model(
email = self.normalize_email(email),
username = username,)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, username, password):
self.create_user(email, username, password)
user.is_staff()
user.is_superuser = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(null=False, unique=True)
username = models.CharField(max_length=25, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = ["username",]
So I am implementing restframework simple-jwt authentication,my settings .py is as follows as:
REST_FRAMEWORK={
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]}
my urls.py:
urlpatterns = [
url(r'^api/token/$', TokenObtainPairView.as_view(), name='token_obtain_pair'),
url(r'^api/token/refresh/$', TokenRefreshView.as_view(), name='token_refresh'),]
on login process, it returns error that "detail": "No active account found with the given credentials" all my users were active. I have no clue to sort this out, I need help.Thanks in advance.
Ensure your password is being hashed before it is stored in your db. I ran into the same problem and discovered my passwords were being stored in plain text. Adding the following to my UserSerializer solved the issue
from django.contrib.auth.hashers import make_password
def validate_password(self, value: str) -> str:
"""
Hash value passed by user.
:param value: password of a user
:return: a hashed version of the password
"""
return make_password(value)
Either you did not create a superuser for your Django application or you are provided the wrong credentials for authentication
Did you remember to set in settings:
AUTH_USER_MODEL = 'your_app_name.User'
Also make sure, is_active = True for the user object that you are saving in your serializer, because
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['fullname', 'username', 'email', 'password']
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
# Adding the below line made it work for me.
instance.is_active = True
if password is not None:
# Set password does the hash, so you don't need to call make_password
instance.set_password(password)
instance.save()
return instance
Note ( As per docs )
The login_required decorator does NOT check the is_active flag on a user, but the default AUTHENTICATION_BACKENDS reject inactive users.
It seems my error was being caused by a write_only parameter on my password field
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length=68, min_length=6, write_only=True)
class Meta:
model = User
fields = ['email', 'username', 'password']
Removed it:
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length=68, min_length=6)
class Meta:
model = User
fields = ['email', 'username', 'password']
and then it was all sunshine and rainbows after that :-)
try this
from django.contrib.auth.hashers import make_password
class UserManager(BaseUserManager):
def create_user(self, email, username, password, alias=None):
user = self.model(
email = self.normalize_email(email),
username = username,)
user.set_password(make_password(password))
user.save()
return user
def create_superuser(self, email, username, password):
self.create_user(email, username, password)
user.is_staff()
user.is_superuser = True
user.save()
return user
Downgraded manually from PyJWT==2.0.0 to PyJWT==1.7.1 and solved our problem
pip install PyJWT==1.7.1
We are using djangorestframework==3.12.1 and djangorestframework-simplejwt==4.4.0 on our requirements.txt and that gave us automatically the 2.0.0 version dependency.
In my opinion, there is a problem where an email address and username is provided for the serializer, but an email is expected as a username for authentication.
I had the same error too. I also made some kind of custom user and tried to login to get a couple of json web tokens. But I only used email, not username. So what I did in the end and it works for me. Perhaps this example explains something in the place where authentication is done ...
Model and manager like this:
from django.db import models
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import (
AbstractBaseUser,
BaseUserManager
)
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from rest_framework_simplejwt.tokens import RefreshToken
class CustomUserManager(BaseUserManager):
def get_or_create(self, email=None, **kwargs):
allowed_kwargs = ['first_name', 'last_name', 'img', 'about']
if email is not None:
try:
user_obj = super(CustomUserManager, self).get(email=email)
if kwargs:
for k, v in kwargs.items():
setattr(user_obj, k, v)
user_obj.save()
except ObjectDoesNotExist:
email = self.normalize_email(email)
user_obj = self.model(email=email)
password = kwargs.pop('password', None)
if password is not None:
user_obj.set_password(password)
if kwargs:
for k, v in kwargs.items():
if k in allowed_kwargs:
setattr(user_obj, k, v)
user_obj.save()
else:
return False
return user_obj
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), null=True, unique=True)
first_name = models.CharField(max_length=150, null=True, blank=True)
last_name = models.CharField(max_length=150, null=True, blank=True)
img = models.URLField(null=True, blank=True)
about = models.TextField(_('about'), max_length=500, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return f'<CustomUser(id={self.id} - email={self.email})>'
class Meta:
ordering = ['-created_at']
#property
def full_name(self):
if hasattr(self, 'first_name') and hasattr(self, 'last_name'):
return f'{self.first_name} {self.last_name}'
return 'No fullname'
#property
def jwt_tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
Customizing token serializer:
from django.contrib.auth import authenticate
from django.contrib.auth.models import update_last_login
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import serializers
class CustomTokenSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
email = serializers.EmailField(required=True)
password = serializers.CharField(min_length=8, write_only=True)
def validate(self, email, password):
try:
self.user = CustomUser.objects.get(email=email)
except ObjectDoesNotExist as e:
message = {'error': f'User with email={email} does not exist.'}
return message
check_auth = authenticate(username=email, password=password)
if check_auth is None:
message = {'error':
'The user exists, but the password is incorrect.'}
return message
data = self.user.jwt_tokens
update_last_login(None, self.user)
return data
Urls:
urlpatterns += [
path('login/token/', views.LoginTokenView.as_view(), name='login-token')
]
I faced a similar issue. Turns out that I had not included the password field among the fields in the Writer serializer.
Before I had code like this;
class UserWriterSerializer(serializers.ModelSerializer):
class Meta:
model = AppUser
fields = [
'id',
'username',
'first_name',
'last_name',
'email',
'is_active',
'telephone',
'userType',
'gender',
'registration_date'
]
Then I added the password field to have this;
class UserWriterSerializer(serializers.ModelSerializer):
class Meta:
model = AppUser
fields = [
'id',
'username',
'password',
'first_name',
'last_name',
'email',
'is_active',
'telephone',
'userType',
'gender',
'registration_date'
]
So in summary, some different value was being saved in the database which was not the password I added. Thus having that error, because the password you place as an input is not matching with what is in the database. Make sure that the serializer is correct
I am new to django, trying to create a custom registration form. I wanted to add a radio button in the user registration to know whether the user is giving a facebook id or linkedin id. Please help me in completing this.
in my models.py
from django.db import models
from django.contrib.auth.models import User, BaseUserManager, AbstractUser
class Registration(models.Model):
u = models.BooleanField(default=False)
model = User
def __unicode__(self):
return self.u
class CustomUserManager(BaseUserManager):
def create_user(self, u ):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
u = models.BooleanField('u:')
model = User
user = self.model(
u = u
)
user.save(using=self._db)
return user
def create_superuser(self, u):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
u,
)
user.is_admin = True
user.save(using=self._db)
return user
class CustomUser(AbstractUser):
objects = CustomUserManager()
pass
class LinkedInUser(CustomUser):
linkedin_id = models.CharField(max_length=30, unique=True)
class Meta:
verbose_name = 'LinkedIn User'
class FacebookUser(CustomUser):
facebook_id = models.CharField(max_length=30, unique=True)
class Meta:
verbose_name = 'Facebook User
in my forms.py
from django import forms
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from .models import CustomUser, LinkedInUser, FacebookUser
import re
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model
class RegistrationForm(forms.ModelForm):
username = forms.RegexField(regex=r'^\w+$', widget=forms.TextInput(attrs=dict(required=True, max_length=30)), label=_("username"), error_messages={ 'invalid': _("This value must contain only letters, numbers and underscores.") })
email = forms.EmailField(widget=forms.TextInput(attrs=dict(required=True, max_length=30)), label=_("Email address"))
password1 = forms.CharField(widget=forms.PasswordInput(attrs=dict(required=True, max_length=30, render_value=False)), label=_("Password"))
password2 = forms.CharField(widget=forms.PasswordInput(attrs=dict(required=True, max_length=30, render_value=False)), label=_("Password (again)"))
CHOICES = (
('LinkedInUser', 'LinkedInUser'),
('FacebookUser', 'FacebookUser'),
)
u = forms.ChoiceField(choices=CHOICES, widget=forms.RadioSelect())
# _ = forms.TypedChoiceField(
# coerce=lambda x: x == 'True',
# choices=((LinkedInUser, 'LinkedInUser'), (FacebookUser, 'FacebookUser')),
# widget=forms.RadioSelect
# )
class Meta :
User = get_user_model()
def clean_name(self):
try:
user = User.objects.get(username__iexact=self.cleaned_data['username'])
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError(_("The username already exists. Please try another one."))
def clean(self):
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError(_("The two password fields did not match."))
return self.cleaned_data
class CustomUserCreationForm(UserCreationForm):
def clean_username(self):
# Since User.username is unique, this check is redundant,
# but it sets a nicer error message than the ORM. See #13147.
username = self.cleaned_data["username"]
try:
CustomUser._default_manager.get(username=username)
except CustomUser.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
class LinkedInUserCreationForm(UserCreationForm):
linkedin_id = forms.CharField(label="LinkedIn Id", max_length=30, required=True,
help_text="Required. 30 characters or fewer.")
class Meta:
User = get_user_model()
model = LinkedInUser
fields = [
'linkedin_id'
]
class FacebookUserCreationForm(UserCreationForm):
facebook_id = forms.CharField(label="Facebook Id", max_length=30, required=True,
help_text="Required. 30 characters or fewer.")
class Meta:
User = get_user_model()
model = FacebookUser
fields = [
'facebook_id'
]