I created a custom User model like the following:
class User(AbstractBaseUser):
class Types(models.TextChoices):
CUSTOMER = 'CUSTOMER', 'Customer'
SUPPLIER = 'SUPPLIER', 'Supplier'
OPERATOR = 'OPERATOR', 'Operator'
base_type = Types.CUSTOMER
objects = UserManager()
phone = models.IntegerField(unique=True)
code = models.IntegerField(blank=True, null=True)
type = models.CharField(max_length=20, choices=Types.choices, default=base_type)
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'phone'
REQUIRED_FIELDS = []
class Meta:
verbose_name = 'user'
verbose_name_plural = 'users'
def __str__(self):
return str(self.phone)
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
@property
def is_staff(self):
return self.is_admin
And a manager for the User model:
class UserManager(BaseUserManager):
def create_user(self, phone, code=663322, password=None):
if not phone:
raise ValueError('Users must have a phone number')
user = self.model(
phone=phone,
code=code,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, phone, code=663322, password=None):
user = self.create_user(
phone=phone,
code=code,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
I wanted to have 3 different user types (customer, supplier, operator) so I created 3 profile models and having a OneToOneField to the base user model for each profile model (with some specific fields related to the user type). I also created 3 proxy models like so:
class Customer(User):
objects = CustomerManager()
base_type = User.Types.CUSTOMER
class Meta:
proxy = True
class Supplier(User):
objects = SupplierManager()
base_type = User.Types.SUPPLIER
class Meta:
proxy = True
class Operator(User):
objects = OperatorManager()
base_type = User.Types.OPERATOR
class Meta:
proxy = True
Their managers are all like this one(only get_queryset method differs):
class SupplierManager(models.Manager):
def create(self, phone, code=223322, password=None):
if not phone:
raise ValueError('Users must have a phone number')
user = self.model(
phone=phone,
code=code,
type='SUPPLIER',
)
user.set_password(password)
user.save(using=self._db)
return user
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(type='SUPPLIER')
Each user type has its own way of signing up so I made 3 different authentication backends(OperatorBackend is same as other two):
class CustomerBackend(BaseBackend):
def authenticate(self, request, phone=None, code=None):
if phone and code:
try:
user = Customer.objects.get(phone=phone)
if user.code == code:
return user
return None
except Customer.DoesNotExist:
return None
def get_user(self, user_id):
try:
return Customer.objects.get(pk=user_id)
except Customer.DoesNotExist:
return None
class SupplierBackend(BaseBackend):
def authenticate(self, request, phone=None, first_name=None):
if phone and first_name:
try:
user = Supplier.objects.get(phone=phone)
if user.sprofile.first_name == first_name:
return user
return None
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
Supplier.objects.get(pk=user_id)
except Supplier.DoesNotExist:
return None
And here are my views for loging users in(authenticate is django's authenticate method):
def customer_login(request):
if request.method == 'POST':
form = CustomerLoginForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = authenticate(request, phone=cd['phone'], code=cd['code'])
if user is not None:
login(request, user, backend='accounts.backends.CustomerBackend')
messages.success(request, 'Logged in', 'success')
return redirect('accounts:home')
else:
messages.error(request, 'User is None', 'danger')
else:
form = CustomerLoginForm()
context = {
'form': form,
}
return render(request, 'accounts/customer_login.html', context)
def supplier_login(request):
if request.method == 'POST':
form = SupplierLoginForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = authenticate(request, phone=cd['phone'], first_name=cd['first_name'])
if user is not None:
login(request, user, backend='accounts.backends.SupplierBackend')
messages.success(request, 'Logged in', 'success')
return redirect('accounts:home')
else:
messages.error(request, 'User is None', 'danger')
else:
form = SupplierLoginForm()
context = {
'form': form,
}
return render(request, 'accounts/supplier_login.html', context)
My problem is when I want to log in with a user type of CUSTOMER, it works and logs the user in. But when I try to log in with another user type, it doesn't work and request.user returns AnonymousUser. I have registered custom authentication backends (django's default ModelBackend is still there) and AUTH_USER_MODEL in settings.py file and auth backends does not seem to be the problem since they return the user when correct credentials are given. I tried creating new users both from admin panel and through managers, and it did not make any difference. Maybe I am going the wrong direction with this kind of implementation, I'm still new in programming. Any help is appreciated.
Edit: I tried accessing user's session_key and backend after logging in and it turns out the login function actually works. But request.user.is_authenticated returns False for some reason when using SupplierBackend.