Create custom user with different roles, fields and permissions

I tried to create custom user with AbstractBaseUser in django:

class UserManager(BaseUserManager):
    """
    Custom user manager that allows you to create new users
    """

    def create_user(self, username, password, **extra_fields):
        """
        Creates and saves a new user with the given username and password
        """
        if not username:
            raise ValueError(_('Username is required'))
        user = self.model(username=username, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(self, username, password, **extra_fields):
        """
        Creates and saves a new superuser with the given username and password
        """
        extra_fields.setdefault('is_active', True)
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('is_staff=True'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('is_superuser=True'))

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


class User(AbstractBaseUser, PermissionsMixin):
    ROLE_CHOICES = (
        ('admin', 'Admin'),
        ('teacher', 'Teacher'),
        ('parent', 'Parent'),
        ('student', 'Student')
    )

    username = models.CharField(max_length=50, unique=True)
    role = models.CharField(max_length=10, choices=ROLE_CHOICES)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __str__(self):
        return self.username

I want users to be able to have only one role, for example, they can only be students or teachers.
I know that I can use groups to give different permissions to each user, but my problem is that each type of user has different fields, for example, the fields that a teacher can have are different from a student, and I don’t know how to do this.
Of course, users can have common fields. For example, I created the following model for common fields:

class Profile(models.Model):
    image = models.ImageField(upload_to='profile_pics/', blank=True, null=True)
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profiles')
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    national_code = models.CharField(max_length=10, validators=[national_code_validator], unique=True, blank=True,
                                     null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def get_full_name(self):
        return f'{self.first_name} {self.last_name}'

    def __str__(self):
        return self.get_full_name()

But I don’t know what to do to have different fields in the Profile for different roles in addition to these common fields. please help.

Do you have a Profile View and Template? Can you share those?

Furthermore, you may enjoy django-allauth module. It does most of the work you did above. All you needed to do was to subclass the AbstractUser, add extra fields and register your new User class in settings.

You didn’t need to create a model a model for the Profile. Just point the view to the User model.

No, I’m just creating models right now
AbstractUser has extra fields like email and etc. that I don’t need them. It would be great if the extra fields could be removed, but I don’t think it’s possible
Can you explain more? For example, I want to create phone number field for teacher and grade for student. Each has different fields. How to do this?

If both teachers and Students are authenticating (login), then one User model could do it while the other properties go to a related model.


class User(AbstractUser):
   """For login authentication"""

class Teacher(models.Model):
    userfk = models.ForeignKey(User, on-delete=CASCADE....)
    other fields here ...

class Student(models.Model):
    userfk = models.ForeignKey(User, on-delete=CASCADE....)
    other fields here ...

That way, you can vary the related models but keep common attributes in User