Django Custom User model AbstractBaseUser and Proxy models. Password is not hashed or set properly

52 views Asked by At

When user is registered with a form or in admin the password is not hashed, as with the superuser.

enter image description here

Any help appreciated. I cannot seem to figure this out. Oh, I am not able to login either. Exept from superuser.

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'theapp',
    'rest_framework',
    'userapp',  


]
.
.
.
AUTH_USER_MODEL = "userapp.UserAccount"

model.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager



class UserAccountManager(BaseUserManager):
    def create_user(self, email, password=None):
        if not email or len(email) <= 0:
            raise ValueError("Email field is required !")
        if not password:
            raise ValueError("Password is must !")

        user = self.model(
            email=self.normalize_email(email),
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password):
        user = self.create_user(
            email=self.normalize_email(email),
            password=password
        )
        user.is_admin = True
        user.is_staff = True
        user.is_superuser = True
        user.save(using=self._db)
        return user



class UserAccount(AbstractBaseUser):
    class Types(models.TextChoices):
        STUDENT = "STUDENT", "student"
        TEACHER = "TEACHER", "teacher"

    type = models.CharField(
        max_length=8, choices=Types.choices, default=Types.TEACHER)
    email = models.EmailField(max_length=200, unique=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    phone_no = models.CharField(max_length=10)

    # special permission which define that
    # the new user is teacher or student
    is_student = models.BooleanField(default=False)
    is_teacher = models.BooleanField(default=False)

    USERNAME_FIELD = "email"

    # defining the manager for the UserAccount model

    objects = UserAccountManager()

    def __str__(self):
        return str(self.email)

    def has_perm(self, perm, obj=None):
        return self.is_admin

    def has_module_perms(self, app_label):
        return True

    def save(self, *args, **kwargs):
        if not self.type or self.type == None:
            self.type = UserAccount.Types.TEACHER
        return super().save(*args, **kwargs)


class StudentManager(models.Manager):
    def create_user(self, email, password=None):
        if not email or len(email) <= 0:
            raise ValueError("Email field is required !")
        if not password:
            raise ValueError("Password is must !")
        email = email.lower()
        user = self.model(
            email=email
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def get_queryset(self, *args, **kwargs):
        queryset = super().get_queryset(*args, **kwargs)
        queryset = queryset.filter(type=UserAccount.Types.STUDENT)
        return queryset


class Student(UserAccount):
    class Meta:
        proxy = True
    objects = StudentManager()

    def save(self, *args, **kwargs):
        self.type = UserAccount.Types.STUDENT
        self.is_student = True
        return super().save(*args, **kwargs)


class TeacherManager(models.Manager):
    def create_user(self, email, password=None):
        if not email or len(email) <= 0:
            raise ValueError("Email field is required !")
        if not password:
            raise ValueError("Password is must !")
        email = email.lower()
        user = self.model(
            email=email
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def get_queryset(self, *args, **kwargs):
        queryset = super().get_queryset(*args, **kwargs)
        queryset = queryset.filter(type=UserAccount.Types.TEACHER)
        return queryset


class Teacher(UserAccount):
    class Meta:
        proxy = True
    objects = TeacherManager()

    def save(self, *args, **kwargs):
        self.type = UserAccount.Types.TEACHER
        self.is_teacher = True
        return super().save(*args, **kwargs)

views.py

from django.shortcuts import render, redirect
from userapp.forms import StudentRegistrationForm, TeacherRegistrationForm

def register_student(request):
    if request.method == 'POST':
        form = StudentRegistrationForm(request.POST)

        if form.is_valid():
            form.save()
            # Redirect to a success page or login page
            return redirect('login-user')
    else:
        form = StudentRegistrationForm()

    return render(request, 'userapp/student_register.html', {'form': form})


def register_teacher(request):
    if request.method == 'POST':
        form = TeacherRegistrationForm(request.POST)
        if form.is_valid():
            form.save()
            # Redirect to a success page or login page
            return redirect('login-user')
    else:
        form = TeacherRegistrationForm()

    return render(request, 'userapp/teacher_register.html', {'form': form})

forms.py

from django import forms
from .models import UserAccount, Student, Teacher


class StudentRegistrationForm(forms.ModelForm):
    class Meta:
        model = Student
        fields = ['email', 'password']


class TeacherRegistrationForm(forms.ModelForm):
    class Meta:
        model = Teacher
        fields = ['email', 'password']

urls.py

from django.urls import path, include
from userapp import views
from django.contrib.auth.views import LoginView

urlpatterns = [
    path('login/', LoginView.as_view(template_name="userapp/loginpage.html"),
         name="login-user"),
    path('register/student/', views.register_student, name='register_student'),
    path('register/teacher/', views.register_teacher, name='register_teacher'),


]

student_register.html

<!DOCTYPE html>
<html>

<body>
  <h2>Student Registration</h2>
  <form method="post" action="{% url 'register_student' %}">
    {% csrf_token %}
    <!-- {{ form.as_p }} -->


    <div class="container">
      <h1>Register Student</h1>
      <p>Please fill in this form to create an account.</p>
      <hr>

      <label for="email"><b>Email</b></label>
      <input type="text" placeholder="Enter Email" name="email" id="email" required>

      <label for="psw"><b>Password</b></label>
      <input type="password" placeholder="Enter Password" name="password" id="password" required>



      <hr>

      
      <button type="submit" class="registerbtn">Register</button>
    </div>

    </div>


    <!-- <button type="submit">Register</button> -->
  </form>
</body>

</html>

Been searching for an answer ....

Tried to change in models (see below) without any effect and without really understanding....

user.set_password(password)
...
user.set_password(self.cleaned_data["password"]) 

Thanks a lot.

1

There are 1 answers

3
SamSparx On BEST ANSWER

Your form is calling the model's .save() and not the manager's .create_user(). The Form instance is an model instantiated from the form's fields, but it hasn't been through the create_user() process.

Because the registration form will (presumably) only ever be creating a user, you could try something like the below :

if form.is_valid():
    Student.objects.create_user(form.cleaned_data["email"],password=form.cleaned_data["password"])