Django multiple users, backend and generic views - python

I'm trying to learn auth django system and I have several questions.
I want to create multiple users with email authentication. So, I used AbtractBaseUser and AbtractBaseManager. I read django doc and this question on the website : Implementing multiple user types with Django 1.5
and I would like to use it. So, I try to implement the second point : all fields for my two user types are in 1 models and with user_type variable I can choose fields to show. My code is down below :
models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
TYPE_USER = (
('student', _('Student')),
('employee', _('Employee')),
)
class MyuserManager(BaseUserManager):
def create_user(self, email, last_name, first_name, date_of_birth, user_type, password=None):
if not email:
raise ValueError("users must have an email address")
user = self.model(
email = self.normalize_email(email),
last_name = last_name,
first_name = first_name,
date_of_birth = date_of_birth,
user_type = user_type,
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, last_name, first_name, date_of_birth, user_type):
user = self.create_user(email,
password = password,
date_of_birth = date_of_birth,
last_name = last_name,
first_name = first_name,
user_type = user_type,
)
user.is_admin = True
user.is_staff = True
user.save()
return user
class MyUser(AbstractBaseUser):
"""Based model user"""
email = models.EmailField(
verbose_name = 'email',
max_length=255,
unique=True,
)
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30)
date_of_birth = models.DateField()
user_type = models.CharField(_('Type'), choices=TYPE_USER, max_length=20, default='student')
phone = models.CharField(max_length=20, blank=True)
friends = models.ManyToManyField("self", blank=True)
faculty = models.ForeignKey(Faculty, null=True, blank=True)
desk = models.CharField(max_length=30, blank=True)
campus = models.ForeignKey(Campus, null=True, blank=True)
job = models.ForeignKey(Function, null=True, blank=True)
cursus = models.ForeignKey(Cursus, null=True, blank=True)
year = models.IntegerField(null=True, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = MyuserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['last_name', 'first_name', 'date_of_birth', 'user_type']
Question : Do you think it's correct ? or should i create 1 class with common fields and 2 classes for distinct type of user ?
Next, i would like to use my own backend so i implement a backend.py file in my project
backend.py
from fbLike.models import MyUser
from django.contrib.auth.backends import ModelBackend
class EmailAuthBackend(ModelBackend):
def authenticate(self, email=None, password=None, **kwargs):
try:
user = MyUser.objects.get(email=email)
if user.check_password(password):
return user
except MyUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return MyUser.objects.get(pk=user_id)
except:
return None
For me, it seems that is a correct implementation.
Questions : But, how to proceed with multi-table inheritence ? is it possible ? how to know what is the type of user before check password if i have for example :
class BaseUser(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
# common fields
class Student(BaseUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
# ...
class employee(BaseUser):
company_name = models.CharField(max_length=100)
# ...
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users.
Below my files :
forms.py
class StudentProfileForm(forms.ModelForm):
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
cursus = forms.ModelChoiceField(Cursus.objects.all())
year = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'cursus', 'year'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
class EmployeeProfileForm(forms.ModelForm):
date_of_birth = forms.DateField(widget = AdminDateWidget)
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
job = forms.ModelChoiceField(Function.objects.all())
desk = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'job', 'desk'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
Here, I think it's too complex but i don't know how to reduce complexity.
views.py
class RegisterProfile(CreateView):
model = MyUser
template_name = 'fbLike/user_profile.html'
form_class = StudentProfileForm
second_form_class = EmployeeProfileForm
def get_object(self):
return super(RegisterProfile, self).get_object()
def get_success_url(self):
return reverse('login',)
def get_context_data(self, **kwargs):
context = super(RegisterProfile, self).get_context_data(**kwargs)
if 'studenForm' not in context:
context['studentForm'] = self.form_class
if 'employeeForm' not in context:
context['employeeForm'] = self.second_form_class
return context
def form_valid(self, form):
self.object = MyUser.objects.create_user(**form)
return super(RegisterProfile, self).form_valid(form)
def form_invalid(self, form):
return super(RegisterProfile, self).form_invalid(form)
def post(self, request, *args, **kwargs):
self.object = None
if request.POST['profileType'] == 'student':
form = self.form_class(request.POST, prefix='st')
form.user_type = 'student'
else:
form = self.second_form_class(request.POST, prefix='em')
form.user_type = 'employee'
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
class UpdateProfile(UpdateView):
model = MyUser
template_name = 'fbLike/modify_profile.html'
def get_form_class(self):
if self.request.user.user_type == 'student':
return StudentProfileForm
return EmployeeProfileForm
def get_success_url(self):
return reverse('welcome',)
For CreateProfile, I would like to send 2 forms in my template and thanks to javscript, i can choose the correct form. But when i submit the form, i have an error with the user password. When i check my users in the admin page, the hashpassword system seems to fail.
So, my first attempt to solve : it was to override save method in my form to use the request.user.set_password but it doesn't work.
I know it's a long explanation. But everyone could give me an example of CreateView class with 2 forms please ? If it isn't possible to use a class but a function how to implement this function ?
I thank you in advance

I will attempt to answer your multiple questions, but please in the future - one question per post. I realize you are new, but StackOverflow is not a discussion forum, have a look at the help section (link at the bottom of each page) specifically the section on asking questions.
I want to create multiple users with email authentication. [...] Do
you think it's correct ? or should i create 1 class with common fields
and 2 classes for distinct type of user ?
Its correct if it works with you. These kinds of questions are better suited for codereview.stackexchange.com. Especially as there isn't a bug.
But, how to proceed with multi-table inheritence ? is it possible ?
how to know what is the type of user before check password if i have
for example [...]
First, authentication backends are different topic altogether; they don't have anything to do with multiple table inheritance (I think you mean relationships or multiple user profiles).
You only need multiple authentication backends if your password authentication algorithm is different for each user type. If all user passwords are stored in the same location/system/database, then you don't need multiple authentication backends.
You should implement a method in your base user class, that returns the type of user. You can then use user_passes_check decorator to restrict access in your views.
This way you will achieve what I think is what you are after "this section only for students", etc.
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users [..] Here, I think it's too complex but i don't know how to reduce
complexity.
You don't need to repeat all the fields of your model in your ModelForm, you only need to override fields if you need to modify them, or to add extra "non-model" fields to the form. You can start with the simple ModelForm, like this:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'password')
A common use of adding an additional field is to ask for password confirmation, by having an extra password field.
For CreateProfile, I would like to send 2 forms in my template and
thanks to javscript, i can choose the correct form. But when i submit
the form, i have an error with the user password. When i check my
users in the admin page, the hashpassword system seems to fail. So, my
first attempt to solve : it was to override save method in my form to
use the request.user.set_password but it doesn't work.
Use a form wizard to implement a multi-step registration process. The first step, you ask the basic information and account type to be created; on the second step, you only send the relevant form instead of sending two forms and then switching them around using javascript.
I don't see anywhere in your code where you use set_password, so can't comment on the second part.

Related

'User' object has no attribute 'user_type' - Django Custom User model issue

I've created a custom user abstract model and profile model to collect additional information once the user registers.
I am collecting "User type: Employer/employee" at the time of registration but this doesn't seem to be recognized in the profile view. Despite the user being correctly added into the DB (I checked via Admin).
For example, I created user: asus23910 (employer user type). But when I login and redirect to http://127.0.0.1:8000/employer_profile/asus23910/, I get following error:
'User' object has no attribute 'user_type'C:\Users\ASUS\PycharmProjects\Content\content\content\views.py, line 112, in employer_profile_view
1. Here's my employer_profile_view.py code:
def employer_profile_view(request, username):
user = User.objects.get(username=username)
if user.user_type != User.EMPLOYER:
# Redirect to the correct profile page if the user type is not employer
return redirect('employee_profile', username=request.user.username)
if request.method == 'POST':
form = EmployerProfileForm(request.POST, instance=user.employerprofile)
if form.is_valid():
employer_profile = form.save(commit=False)
employer_profile.user = user
employer_profile.save()
return redirect('employer_profile', username=request.user.username)
else:
form = EmployerProfileForm(instance=user.employerprofile)
context = {
'form': form,
'username': username,
}
return render(request, 'employer_profile.html', context)
2. Employer Profile model and connector
class EmployerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_type = models.CharField(
max_length=10,
choices=User.USER_TYPE_CHOICES,
default=User.EMPLOYER
)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
title = models.CharField(max_length=255)
company_name = models.CharField(max_length=255)
company_logo = models.ImageField(upload_to='company_logos/')
company_location = models.CharField(max_length=255)
company_website = models.URLField()
company_twitter = models.URLField()
#one-2-one connector
#receiver(post_save, sender=User)
def create_employer_profile(sender, instance, created, **kwargs):
if created:
EmployerProfile.objects.create(user=instance, user_type=instance.user_type)
print('Employer Profile created')
#receiver(post_save, sender=User)
def save_employer_profile(sender, instance, **kwargs):
instance.employerprofile.user_type = instance.user_type
instance.employerprofile.save()
print('Employer Profile saved')
3. User model
#model one to store the user into db
class User(AbstractUser):
EMPLOYER = "employer"
EMPLOYEE = "employee"
USER_TYPE_CHOICES = [
(EMPLOYER, "Employer"),
(EMPLOYEE, "Employee"),
]
user_type = models.CharField(
max_length=10,
choices=USER_TYPE_CHOICES,
default=EMPLOYEE
)
email = models.EmailField(default='example#example.com')
username = models.CharField(max_length=150, default='example_user')
password = models.CharField(max_length=128, default='!')
groups = models.ManyToManyField(
Group,
blank=True,
related_name='content_groups'
)
user_permissions = models.ManyToManyField(
Permission,
blank=True,
related_name='content_user_permissions'
)
`
**
What I've tried:**
Flushing and starting new DB (as I used in-built Django user model before and some old users weren't fairing well with the new user-type field).
Adding the user type with default employer option to employer view and fetching the usertype from user model.
**
What I expect:**
The profile view to connect with the custom user model and allow the user to add additional information to their user profile. And ofcourse the profile page to have the user-type attribute as initially stored from user class.
You probably import User not from your models file, but from django.
Anyway I highly recommend (if you overwrote AUTH_USER_MODEL) using built-in get_user_model() method from Django e.g.:
from django.contrib.auth import get_user_model
def employer_profile_view(request, username):
user = get_user_model().objects.get(username=username)
if user.user_type != User.EMPLOYER:
...
And don't use User name for model, I prefer to use CustomUser by myself but you can name it differently, just to avoid mistakes.

Adding a simple new account in the django admin doesn’t ask for the correct fields

I’m new to django and am sure this is probably a quick spot for most. I essentially want to:
Change the default User model to use an email (and password) to log in (instead of username)
Add extra fields to the default User model
I’ve followed a video tutorial that resulted in me writing this code:
Models.py:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have a password")
user = self.model(
email=self.normalize_email(email), # converts chars to lowercase
password=password,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password): # password=None?
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 Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email",
max_length=100, unique=True)
date_joined = models.DateTimeField(
verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=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)
first_name = models.CharField(max_length=100, null=True, blank=True)
last_name = models.CharField(max_length=100, null=True, blank=True)
# username field unwanted - only added to get rid of fielderror "Unknown field(s) (username) specified for Account. Check fields/fieldsets/exclude attributes of class AccountAdmin."
username = models.CharField(max_length=30, unique=True, blank=True)
# #todo: add more new fields
USERNAME_FIELD = 'email' # so users use email to log in
REQUIRED_FIELDS = ['password']
objects = MyAccountManager()
def __str__(self): # add -> str: type?
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
Admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from accounts.models import Account
class AccountAdmin(UserAdmin):
list_display = ('email', 'date_joined', 'is_staff', 'is_admin')
search_fields = ('email', 'date_joined')
readonly_fields = ('date_joined', 'last_login') # make fields immutable
filter_horizontal = ()
list_filter = ()
fieldsets = ()
ordering = ()
admin.site.register(Account, AccountAdmin)
My questions are as follows:
When using the createsuperuser command, why does it only require me to type in a password once? (I thought it should ask again to confirm; it just asks for email and password fields once)
When clicking on ‘add account’ in django admin, why doesn’t it ask for an email and password? (It asks for an un-required username and required password (and 2nd password-retype)
The point of these classes is to just try to user the default User model (instead of creating my own Account class for example) and edit it to rid the username field and add other new fields. Am I overcomplicating the solution here and, instead, should I just create a new class that simply extends the User model like the example below?
class Account(User):
new_field = models.BooleanField(default=False)
objects = UserManager()
Is this way just simpler and better for me: https://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
When clicking ‘add account’ in the django admin, does it try to create a new super user, or just a new instance of the MyAccountManager, Account or AccountAdmin class?
Thanks for helping a beginner out here.
The application does what you have told it to do, and as per your MyAccountManager class you have to enter the password only once.
I would think that the problem occurs in the admin.py bit. Your AccountAdmin inherits from UserAdmin class, which aims at serving the Django User model. In order to work with your custom user model, use ModelAdmin.
from django.contrib import admin
from accounts.models import Account
class AccountAdmin(admin.ModelAdmin):
list_display = ('email', 'date_joined', 'is_staff', 'is_admin')
search_fields = ('email', 'date_joined')
readonly_fields = ('date_joined', 'last_login') # make fields immutable
filter_horizontal = ()
list_filter = ()
fieldsets = ()
ordering = ()
admin.site.register(Account, AccountAdmin)
I would say the Django User model will not work for you, because you need to set a unique email field, get rid of the username, and use email when logging the user in. As per Django documentation in this situation you need to implement a custom user model.

Creating multiple types users in django rest framework, got an unexpected keyword argument 'last_login'

I am trying to learn Django, and I am creating various cases just to exercise and hopefully learn something new. Suppose that I am trying to create different users and assign different permissions latter, but first lets try to create them first. I was researching last night and it is a common problem and many differnt implementations. Most common that I have seen is with student, teacher, admin. My case is similar, but instead of teacher it is Staff as staff member. Staff inherits from User, which itself inherits from AbstractBaseUser. Below is my code for each class, model and serializer.
User mode:
class User(AbstractBaseUser):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
email = models.EmailField(db_index=True, unique=True)
username = models.CharField(max_length=40, unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name','username']
objects = UserManager()
## And user manager:
class UserManager(BaseUserManager):
def get_by_natural_key(self, email):
return self.get(email=email)
Model:
class Staff(User):
qualification = models.CharField(db_index=True, max_length=255)
department = models.CharField(db_index=True, max_length=10)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'department','username']
objects = StaffManager()
def __str__(self):
return self.first_name
Staff Model Manager:
class StaffManager(BaseUserManager):
def create_staff(self, first_name, last_name, email, qualification, department, password=None):
if email is None:
raise TypeError('Users must have an email address.')
staff = Staff(first_name=first_name, last_name=last_name,
email=self.normalize_email(email),
qualification=qualification, department=department)
staff.set_password(password)
staff.save()
return staff
And the Staff Registartion Serializer:
class StaffRegistrationSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length=128,
min_length=8,
write_only=True
)
token = serializers.CharField(max_length=255, read_only=True)
class Meta:
model = Staff
fields = '__all__'
def create(self, validated_data):
return Staff.objects.create_staff(**validated_data)
The problem is that from the API, a field of Last login is created that it is not declared in models. And when I submit my form, I get this error:
TypeError: create_staff() got an unexpected keyword argument 'last_login'
[17/Dec/2020 15:44:43] "POST /api/auth/register-staff HTTP/1.1" 500 104579
I was looking at this which has the similar problem, but he has a different implementation and at this point, I do not fully understand so I could modify my code to act in the same way.
P.S, I am folloing this tutorial to create multiple types of users.
The problem is that **validated_data has last_login with a value. So when you're passing in the kwargs of validated_data, it gets expanded into the function arguments for create_staff() but there's no slot for last_login.
The fix is to add , last_login=None to the definition of create_staff, and pass it along to the new Staff object. E.g:
def create_staff(self, first_name, last_name, email, qualification, department, password=None, last_login=None):
# code cut for brevity
staff = Staff(first_name=first_name, last_name=last_name,
email=self.normalize_email(email),
qualification=qualification, department=department,
last_login=last_login)

DRF serializer update nested user

i'm using Django 1.11 and the Django Rest Framework in order to create an API, where a user can create and update an employee which is related to the django user model.
The stripped down code i've got looks like this:
I've got this model:
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employee')
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
these two viewsets:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = serializers.UserSerializer
permission_classes = [permissions.IsAuthenticated]
and
class EmployeeViewSet(viewsets.ModelViewSet):
serializer_class = serializers.EmployeeSerializer
permission_classes = [permissions.IsAuthenticated]
and these two serializers:
class UserSerializer(serializers.HyperlinkedModelSerializer)
class Meta
models = User
fields = ('url', 'id', 'username', 'email', 'is_staff', 'first_name', 'last_name', 'password')
read_only_field = ('id',)
def validate(self, data)
# ... Check if data is valid and if so...
return data
and
class EmplyoeeSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = Employee
field = ('url', 'uuid', 'user')
read_only_field = ('uuid')
def validate(self, data):
return data
def create(self, validated_data):
user = User(**validated_data['user'])
user.save()
employee = Employee(user=user)
employee.save()
return employee
def update(self, employee, user):
employee.user.username = user.username
employee.user.email = user.email
employee.user.first_name = user.first_name
employee.user.last_name = user.last_name
employee.user.is_staff = user.is_staff
# ... Check if password has changed, and if so...
employee.user.set_password(user.password)
employee.user.save()
employee.save()
return employee
also i've got these two routers in my urls.py
router = routers.DefaultRouter()
router.register(r'api/user', views.UserViewSet, base_name='user')
router.register(r'api/employee', views.UserViewSet, base_name='employee')
Creating and updating an instance of user is no problem.
I can also create an employee which in return will create an user and then an employee assigend to that user.
I can even update the username of the employee and the user will get updated too,
but i can't update first_name, last_name, email, is_staff and password.
DRF keeps telling me that the username is already taken, but when i change the username and other information like first_name and last_name and then send a PUT request to the server, the employee and associated user instance are getting updated properly.
What am i missing?
Why can't i update the employees user instance like i can update the normal user instance when i'm at the user api endpoint? :/
Thanks in advance,
any help would be appreciated.
Finally i found out what i was missing.
The UserSerializers adds django.contrib.auth.validators.UnicodeUsernameValidator and UniqueValidator to the field username which gets checked every time i do a put request.
Adding validators = [] to the Meta Class of UserSerializer solved my problem.

Custom user model and custom authentication not working in django. Authentication form is not passing validation tests

I am trying to implement the custom User model and custom authentication for my application.
I am able to create models and make migrations. But when I created a form and implemented the login template, I am getting this error -
login
User model with this Login already exists.
View where I am authenticating user -
from django.shortcuts import render_to_response, redirect, render
from django.template import RequestContext
from django.contrib.auth import login, logout , authenticate
from accounts.forms import AuthenticationForm
from django.contrib.auth.decorators import login_required
def accounts_login(request):
context = {}
if request.method == "POST":
form = AuthenticationForm(request.POST) #getting error here
print(form)
if form.is_valid():
user = authenticate(login = request.POST["login"], password = request.POST["password"])
if user is not None:
print(user)
login(request,user)
next_url = request.POST["next"]
return redirect(next_url, args=(), kwargs={})
#more code - not required here. Let me know if needed.
Authentication form:
from django import forms
from accounts.models import UserModel
class AuthenticationForm(forms.Form):
login = forms.CharField(max_length=128, required=True)
password = forms.CharField(max_length=128, required=True)
My custom user model and user manager -
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.utils import timezone
from django.db.models import Max
class MyUserManager(BaseUserManager):
use_in_migrations = True
def create_user(self,login, parent_type, last_name, first_name, password):
return create_superuser(self,login, parent_type, last_name, first_name, password)
def create_superuser(self,login, parent_type, last_name, first_name, password):
maxx = self.model.objects.all().aggregate(Max('sys_id'))
print(maxx)
user = self.model(
sys_id = maxx["sys_id__max"] + 1,
login = login,
password = password,
parent_type = parent_type,
last_name = last_name,
first_name = first_name,
display_name = last_name + " " + first_name,
created_when = timezone.now()
)
user.save(using=self._db)
# no difference here...actually can set is_admin = True or something like that.
return user
class UserModel(AbstractBaseUser):
# custom user class
SYSTEM = 0
TENANT = 1
parent_type_choices = (
(SYSTEM, 'System'),
(TENANT, 'Tenant')
)
sys_id = models.BigIntegerField(primary_key=True, blank=True)
parent_type = models.PositiveIntegerField(choices=parent_type_choices, null=False, blank=False)
parent_sys_id = models.ForeignKey('tenant.TenantModel', on_delete = models.SET_NULL, null=True, blank=True)
last_name = models.CharField(null=False, blank=False, max_length=40)
first_name = models.CharField(max_length=40, null=False, blank=False)
display_name = models.CharField(max_length=80, unique=True, null=False, blank=True)
login = models.CharField(max_length=40, unique=True, null=False, blank=False)
authentication_method = models.CharField(max_length=80, null=True, blank=True)
access_valid_start = models.DateTimeField(null=True, blank=True)
access_valid_end = models.DateTimeField(null=True, blank=True)
created_when = models.DateTimeField(null=True, blank=True, )
created_by = models.BigIntegerField(null=True, blank=True)
last_updated_when = models.DateTimeField(null=True, blank=True)
last_updated_by = models.BigIntegerField(null=True, blank=True)
notes = models.CharField(max_length=2048, null=True, blank=True)
is_active = models.BooleanField(default=True)
objects = MyUserManager()
USERNAME_FIELD = "login"
# REQUIRED_FIELDS must contain all required fields on your User model,
# but should not contain the USERNAME_FIELD or password as these fields will always be prompted for.
REQUIRED_FIELDS = ['parent_type', 'last_name', 'first_name']
class Meta:
app_label = "accounts"
db_table = "Users"
def __str__(self):
return self.display_name
def get_full_name(self):
return self.display_name
def get_short_name(self):
return self.last_name
def check_password(self,password):
return True
if self.password ==password:
return true
Custom backend
from django.conf import settings
from accounts.models import UserModel
class MyAuthBackend(object):
def authenticate(self, login, password):
try:
user = UserModel.objects.get(login=login)
if user.check_password(password):
return user
else:
print("wrong password")
return None
except User.DoesNotExist:
return None
except Exception as e:
print(repr(e))
return None
def get_user(self, user_id):
try:
user = User.objects.get(pk=user_id)
if user.is_active:
return user
return None
except User.DoesNotExist:
return None
Added these two variables in setting.py
AUTH_USER_MODEL = 'accounts.UserModel'
AUTHENTICATION_BACKENDS = ('accounts.backends.MyAuthBackend',)
Getting backend from command line
>>> get_backends()
[<accounts.backends.MyAuthBackend object at 0x7f2c438afe80>]
A user with username lets say xyz and password 'ABC' exists in db.
1. When I am trying to login with existing username and password it throws me error User model with this Login already exists.
2. When I try to login with non-existing username and password (lets say xyz123) it throws me error authenticate() takes 0 positional arguments but 2 were given
I followed multiple articles but mainly django official documentation for this. Read multiple articles to solve the issue but failed.
Please suggest what I am doing wrong.
Update 1: chnged the AuthenticationForm from ModelForm to normal Form class. Error 1 is solved.
Update 2: Not using my custom backend as pointed by Daniel, it is useless because I am not doing anything new in it. Hence error 2 is also solved.
Update 3: Now hashing password before saving user to db in create super user function. user.set_password(password)
Firstly, you are storing passwords in plain text in your overwritten create_superuser method. You must absolutely not do this. Quite apart from the security issues, it won't actually work, as the authentication backend will hash a submitted password before comparing it with the saved version, so it will never match.
Secondly, your actual problem is that your AuthenticationForm is a ModelForm; as such it will attempt to see if it can create a user with that email and password. There is no reason to make it a modelform, you should just have a plain form with CharFields for email and password.
Note though that you won't actually be able to log in, until you have fixed the plain text password issue. Note also that your custom auth backend - as well as breaking the authenticate method by not conforming to the interface described in the docs - does not do anything that the built-in one does not do; it is pointless and you should remove it.

Categories

Resources