User Password displayed in plain text

User Password displayed in plain text

User can't login - Django

This is a problem peculiar to newbies in Python Django like me and as promised, I will put out major errors encountered on this journey in the hope that you do not go through similar pain I did.

This particular error was a pain in the gut as I spent days tiring to figure it out.

Statement of Problem:

I am new to Django and I am trying to create an app that uses Abstract users but when I create a user in the Django admin, the user's password is displayed in plain text as against the standard which is to hash the password.

When I try to log in with the user and password, I get the error message "user details is incorrect".

When you check most use cases on the internet, the first guess is that you have inherited the ModelAdmin instead of the UserAdmin but this is not the case most times.

Note that this error occurs only when you are using a custom user model. I mean if you have customized the user model. We do this when we need more than the fields Django provides by default.

Lets go through my code together to see how i solved the issue.

models.py:

from django.db import models
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import AbstractUser
from phonenumber_field.modelfields import PhoneNumberField
from django.db import models
# Create your models here.


class CustomUserManager(BaseUserManager):

    def create_user(self,email,password,**extra_fields):
        if not email:
            raise ValueError(_('Please enter an email address'))
        email=self.normalize_email(email)
        new_user=self.model(email=email,**extra_fields)
        new_user.set_password(password)
        new_user.save()
        return new_user


    def create_superuser(self,email,password,**extra_fields):


        extra_fields.setdefault('is_staff',True)
        extra_fields.setdefault('is_superuser',True)
        extra_fields.setdefault('is_active',True)


        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff as True'))

        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser as True'))


        if extra_fields.get('is_active') is not True:
            raise ValueError(_('Superuser must have is_active as True'))



        return self.create_user(email,password,**extra_fields)





class User(AbstractUser):
    username=models.CharField(max_length=40,unique=True)
    email=models.EmailField(max_length=80,unique=True)
    phone_number=PhoneNumberField(unique=True,null=True)
    bvn = models.IntegerField(unique=True,null=True)



    REQUIRED_FIELDS=['username','phone_number', 'bvn']
    USERNAME_FIELD='email'


    objects = CustomUserManager()

    def __str__(self):
        return f"User {self.username}"

As you can see here, I have added other fields not supplied in Django defaults fields. I have "phone number", "bvn" and "email". To get this to work you have to add some customization to the model. Hence, the error.

serializers.py:

from rest_framework import serializers

from terms.models import Terms
from .models import User
from phonenumber_field.serializerfields import PhoneNumberField


class UserCreationSerializer(serializers.ModelSerializer):
    username=serializers.CharField(max_length=25)
    email=serializers.CharField(max_length=80)
    phone_number=PhoneNumberField()
    password = serializers.CharField(min_length=8, write_only=True)
    bvn = serializers.IntegerField()

    class Meta:
        model = User
        fields = ['username', 'email', 'phone_number','password', 'bvn']

        def validator(self, attrs):
            username_exist = User.objects.filter(username=attrs['username']).exist()
            if username_exist:
                raise serializers.ValidationError(detail="User with name already exists")

            email_exist = User.objects.filter(username=attrs['email']).exist()
            if email_exist:
                raise serializers.ValidationError(detail="User with email already exists")

            phone_number_exist = User.objects.filter(username=attrs['phone_number']).exist()
            if phone_number_exist:
                raise serializers.ValidationError(detail="User with phone_number already exists")

            bvn_exist = User.objects.filter(username=attrs['bvn']).exist()
            if bvn_exist:
                raise serializers.ValidationError(detail="User with bvn already exists")


            return super().validate(attrs)

        def create(self, validated_data):
            return Terms.objects.create_user(**validated_data)



        def create_superuser(self, email, password):
            user = self.create_user(
            email,
            password=password,
    )

views.py:

from django.shortcuts import render
from rest_framework import generics, status
from rest_framework.response import Response
from . import serializers
from .models import User
import django
# Create your views here.



class UsercreateView(generics.GenericAPIView):
    serializer_class = serializers.UserCreationSerializer



    def post(self, request):
        data = request.data

        serializer = self.serializer_class(data=data)

        if serializer.is_valid():
            try:
                serializer.save()
            except django.db.utils.IntegrityError:
                return Response({"Message": "User aleady exist"},status=status.HTTP_400_BAD_REQUEST )

            return Response(data=serializer.data, status=status.HTTP_201_CREATED)


        return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST)

admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User


admin.site.register(User, UserAdmin)

Solution To solve this issue, we need to adjust our admin.py so as to force those extra fields on the Django Admin.

Adjusted admin.py:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

class UserAdmin(UserAdmin):
    list_display = ['bvn', 'username', 'email']
    add_fieldsets = (

            (None, {

                'classes': ('wide',),

                'fields': ('username', 'email', 'phone_number', 'bvn', 'is_active', 'password1', 'password2'),

            }),

        )

# # Register your models here.
admin.site.register(User, UserAdmin)

Basically, we have added a class in the admin.py to add the fields. The extra fields were provided.

I hope this works for you. Cheers