I am trying to make a small authentication app using django, the admin page
won't work any more, the problem happened after i changed the AUTH_USER_MODEL inside settings.py , now if i typed python manage.py createsuperuser it gives me different fields than it was before, more than that, if i tried to access the admin page, it asks for my phone number as login not username.
Also inside database i can't see any tables that belongs to auth_user, in fact, i did the migration but its just recognizing Client model but not Admin model.
Is there any way to make both of my models work ??
Here are my codes:
settings.py
AUTH_USER_MODEL = 'authiz.Client'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'authiz.models.Client',
)
# Login settings
LOGIN_URL = 'login'
LOGOUT_URL = 'index'
LOGIN_REDIRECT_URL = 'profile'
models.py
class Client(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(null=True, blank=True, unique=True)
phone = models.CharField(max_length=18,unique=True)
joined = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=200, null=True, blank=True)
last_name = models.CharField(max_length=200, null=True, blank=True)
is_staff = models.BooleanField(default=False)
password = models.CharField(default='', null=False, max_length=255)
USERNAME_FIELD = 'phone'
objects = UserManager()
def get_username(self):
return self.first_name
def get_short_name(self):
return self.last_name
def __str__(self):
return self.phone
def natural_key(self):
return (self.get_username(),)
def set_password(self, raw_password):
self.password = make_password(raw_password)
def check_password(self, raw_password):
def setter(raw_password):
self.set_password(raw_password)
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
def set_unusable_password(self):
self.password = make_password(None)
def has_usable_password(self):
return is_password_usable(self.password)
def is_superuser(self):
return False
def is_admin(self):
return False
Related
i have already Django project that completely works fine with custom user model phone and password for registering and sign up,
here i need to add firebase phone number authentication to send sms to user before register to my site ???
prettier any help here .
my custom user model
class MyUser(AbstractBaseUser):
email = models.EmailField(blank=True,null=True, verbose_name='Email Address',max_length=255 )
name = models.CharField(max_length=100,verbose_name= 'Username', )
last_name = models.CharField(blank=True,max_length=100,verbose_name= 'Family Name')
mobile = models.IntegerField(unique=True,verbose_name= 'Mobile Number')
governorate = models.CharField(blank=True,null=True,verbose_name='Governorate',max_length=255)
image =models.ImageField(blank=True,upload_to='profile_pics', default='profile_pics/avatar.png')
Is_Banned = models.BooleanField(default=False)
notification =models.BooleanField(default=False,verbose_name= 'Enable Notification')
language_choices =(
("Arabic", "Arabic"),
("English", "English"),
("Kurdish", "Kurdish"),
)
language = models.CharField( choices = language_choices,blank=True, default = 'Arabic',max_length=50,verbose_name= 'Language')
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'mobile'
REQUIRED_FIELDS = ['name','email']
def __str__(self):
return self.name
class Meta:
verbose_name_plural='Users'
def Adressess(self):
return 'adressess'
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.is_admin
#property
def first_name(self):
return self.name
I'd like to have 3 differents table and use them to authenticate.
The Contact will authenticate himself with his phone number, ChainStore with his identifier and User (django user admin) will have his normal behavior (with username). ChainStore and Contact have forms to authenticate, how can i do ?
Here is my models:
class Contact(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
first_name = models.CharField(max_length=10)
last_name = models.CharField(max_length=10)
address = models.ForeignKey(
Address, on_delete=models.CASCADE, blank=True, null=True
)
email = models.CharField(blank=True, null=True, max_length=140)
mobile = models.CharField(max_length=12, unique=True)
password = models.CharField(max_length=128)
optin_email = models.BooleanField(default=False)
optin_sms = models.BooleanField(default=False)
class ChainStore(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
password = models.CharField(max_length=64)
last_login = models.DateTimeField(blank=True, null=True)
rules = models.JSONField(default=dict)
I've tried to use inheritance with AbstractBaseUser but i had problems like
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'Contact.groups'
My Forms:
class ContactSignUp(forms.Form):
first_name = forms.CharField(max_length=10)
last_name = forms.CharField(max_length=10)
email = forms.EmailField()
mobile = forms.RegexField(max_length=12)
password = forms.CharField(max_length=32)
password_verification = forms.CharField(max_length=32)
class ContactAuth(forms.Form):
mobile = forms.CharField(max_length=12)
password = forms.CharField(max_length=32)
My views:
class ContactSignUpView(FormView):
form_class = ContactSignUp
model = Contact
template_name = "sign_up.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
Contact(
first_name=form.cleaned_data["first_name"],
last_name=form.cleaned_data["last_name"],
email=form.cleaned_data["email"],
mobile=form.cleaned_data["mobile"],
password=form.cleaned_data["password"]
).save()
return super().form_valid(form)
class ContactAuthView(FormView):
form_class = forms.ContactAuth
model = Contact
template_name = "auth.html"
success_url = "/"
def form_valid(self, form):
"""This method is called when valid form data has been POSTed."""
user = ContactBackend().authenticate(
form.cleaned_data["mobile"],
form.cleaned_data["password"],
)
return super().form_valid(form)
My backend:
class ContactBackend(BaseBackend):
"""
Mobile Authentication Backend
Allows a user to sign in using an mobile/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
try:
contact = models.Contact.objects.get(mobile=username)
if check_password(password, contact.password): # Imported from django.contrib.auth.hashers
return contact
except models.Contact.DoesNotExist:
return None
def get_user(self, user_id):
try:
return models.Contact.objects.get(pk=user_id)
except models.Contact.DoesNotExist:
return None
And finally my settings:
AUTH_USER_MODEL = "auth.User"
AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.ModelBackend",
"contact.backends.ContactBackend"
]
Trying to make a password reset view. I'm using the auth_views built in views ie PasswordResetView and PasswordResetConfirmView for resetting password. However I was getting this error
Cannot resolve keyword 'is_active' into field. Choices are: active, admin, email, first_name, id, last_login, last_name, logentry, password, staff, timetables
. Tried changing active to is_active and getting this error.
django.core.exceptions.FieldError: Unknown field(s) (active) specified for User
Not able to make migrations
models.py
class User(AbstractBaseUser):
first_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=50, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True)
is_active = models.BooleanField(default=True)
staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
def get_first_name(self):
if self.first_name:
return self.first_name
return self.email
def get_last_name(self):
return self.last_name
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.staff
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.is_active
forms.py
class UserAdminChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'first_name', 'last_name',
'password', 'active', 'admin')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
Try to change is_active = models.BooleanField(default=True) to active = models.BooleanField(default=True) in your User class, and is_active property
see below
class User(AbstractBaseUser):
first_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=50, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False)
admin = models.BooleanField(default=False)
#property
def is_active(self):
return self.active
I have created 3 custom user models. However only one user under the models Users() is able to login in into a sells dashboard that I have created. I want the two user namelly, Buyer() and Supplier() to be able to login to the dashboard but not to the admin area. The following is my code. Please help me see the error.
# models.py
# These are my three custom models
from django.db import models
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, UserManager, BaseUserManager, PermissionsMixin
from django.conf import settings
# Superuser model
class Users(AbstractUser):
username = models.CharField(max_length=25, unique=True)
email = models.EmailField(unique=True, null="True")
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
# Returns username
def __str__(self):
return self.username
# Supplier user model
class Supplier(AbstractBaseUser):
sname = models.CharField(max_length=255, verbose_name='Supplier Name', unique=True)
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address', null=True)
physical_address = models.CharField(max_length=255, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe yourself')
is_active = models.BooleanField(default=True)
objects = Users()
USERNAME_FIELD = 'sname'
def __str__(self):
return self.sname
# This model save inventory of a supplier
class Inventory(models.Model):
pname = models.CharField(max_length=255, verbose_name='Product Name')
quantity = models.PositiveIntegerField(verbose_name='Quantity (kgs)')
measurement = models.CharField(max_length=255, verbose_name='Measurement')
orginal_price = models.PositiveIntegerField(verbose_name='Original Price')
commission = models.PositiveIntegerField(verbose_name='Commission')
selling_price = models.PositiveIntegerField(verbose_name='Selling Price (MWK)')
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, verbose_name='Supplier')
def __str__(self):
return self.pname
# This model saves the transaction a buyer has made
class Transaction(models.Model):
cust_name = models.CharField(max_length=255, verbose_name='Customer Name')
pid = models.ForeignKey(Inventory, on_delete=models.CASCADE, verbose_name='Product')
quantity_r = models.PositiveIntegerField(verbose_name='Quantity (KGS)')
success = models.BooleanField(default=False)
uid = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name='User Id')
# Calculates actual price
def actual_price(self):
return int(self.quantity_r) * int(self.pid.selling_price)
# Returns customer name
def __str__(self):
return self.cust_name
# Calculates total costs of suppliers products
class SupplierProductCostView(models.Model):
id = models.PositiveIntegerField(primary_key=True)
sname = models.CharField(max_length=255)
price = models.PositiveIntegerField()
class Meta:
db_table = 'home_supplierproductcostview'
managed = False
def __str__(self):
return str(self.id) + ' ' + self.sname + ' ' + str(self.price)
# Buyer user model
class Buyer(AbstractBaseUser):
username = models.CharField(max_length=255, unique=True)
company_name = models.CharField(max_length=255, verbose_name='Company Name')
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address')
address = models.CharField(max_length=500, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe your company')
is_active = models.BooleanField(default=True)
objects = Users()
USERNAME_FIELD = 'username'
# Returns username
def __str__(self):
return self.username
# forms.py
# This is my registration and login form
from django import forms
from .models import Supplier, Buyer
# Custom supplier registration form
class SupplierRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput)
# Supplier registration form fields
class Meta:
model = Supplier
fields = ('sname', 'phone_number', 'email_address', 'physical_address', 'description')
# Password check
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError('Passwords don\'t match.')
return cd['password2']
# Custom buyer registration form
class BuyerRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password', widget=forms.PasswordInput)
# Buyer registration form fields
class Meta:
model = Buyer
fields = ('username', 'company_name', 'phone_number', 'email_address', 'address', 'description')
# Password check
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError('Passwords don\'t match.')
return cd['password2']
# Login form
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
#views.py
# These are my login and logout views
from django.shortcuts import render, redirect, reverse
from django.contrib.auth import authenticate, login, logout
from users.forms import LoginForm
# User logs in to dashboard
def index(request):
if request.method == 'POST':
loginform = LoginForm(request.POST)
# Check if form is valid
if loginform.is_valid():
username = loginform.cleaned_data['username']
password = loginform.cleaned_data['password']
user = authenticate(username=username, password = password)
# If user does not exist
if user is not None:
login(request, user)
return redirect(reverse('home'))
else:
context = {
'form':loginform,
'error': 'Could not login, Please try again...',
}
return render(request, 'users/index.html', context)
loginform = LoginForm()
context = {
'form' : loginform,
}
# Returns login form
return render(request, 'users/index.html', context)
# Logout
def logout_user(request):
logout(request)
return redirect(reverse('login'))
I extended the custom user model with a OneToOneField but now I Am getting this error "django.db.utils.IntegrityError: null value in column "users_id" violates not-null constraint" apparently there is something wrong with my registration view. Can you help?
# Buyer registration view
def buyer_form_register(request):
if request.method == 'POST':
buyer_form = BuyerRegistrationForm(request.POST)
if buyer_form.is_valid():
# Create a new user object but avoid saving it yet
new_user = buyer_form.save(commit=False)
# Set the chosen password
new_user.set_password(
buyer_form.cleaned_data['password'])
# Save the User object
new_user.save()
# Create the user profile
return render(request, 'register/register_done.html')
else:
buyer_form = BuyerRegistrationForm()
return render(request, 'register/register_buyer.html', {'buyer_form': buyer_form})
# Edited model
class Buyer(AbstractBaseUser):
# New field
users = models.OneToOneField(Users, on_delete=models.CASCADE)
username = models.CharField(max_length=255, unique=True)
company_name = models.CharField(max_length=255, verbose_name='Company Name')
phone_number = models.CharField(max_length=255, verbose_name='Phone Number')
email_address = models.CharField(max_length=255, verbose_name='Email Address')
address = models.CharField(max_length=500, verbose_name='Physical Address')
description = models.TextField(max_length=255, verbose_name='Describe your company')
objects = Users()
USERNAME_FIELD = 'username'
def __str__(self):
return self.username
You can't have 3 custom user models, in settings.py you can only set AUTH_USER_MODEL to one model.
Extend your user model with profiles (OneToOneField) instead to differentiate between the different user types.
I created a custom user model using AbstractBaseUser class. Code for the same is here.
class UserModel(AbstractBaseUser):
user_type_choices = (
(constants.USER_TYPE_ADMIN, 'Admin'),
(constants.USER_TYPE_INSTITUTE, 'Institute'),
(constants.USER_TYPE_STUDENT, 'Student')
)
sys_id = models.AutoField(primary_key=True, blank=True)
name = models.CharField(max_length=127, null=False, blank=False)
email = models.EmailField(max_length=127, unique=True, null=False, blank=False)
mobile = models.CharField(max_length=10, unique=True, null=False, blank=False)
user_type = models.PositiveSmallIntegerField(choices=user_type_choices, null=False, blank=True, help_text="Admin(1)/Institute(2)/Student(3)")
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)
is_staff = models.BooleanField(default=True)
objects = MyUserManager()
USERNAME_FIELD = "email"
# 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 = ['name', 'mobile', 'user_type']
class Meta:
app_label = "accounts"
db_table = "users"
def __str__(self):
return self.email
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def is_access_valid(self):
if self.access_valid_end > utility.now():
return True
else:
return False
def save(self, *args, **kwargs):
if not self.sys_id:
self.created_when = utility.now()
self.last_updated_when = utility.now()
return super(UserModel, self).save(*args, **kwargs)
Code for its manager is as below.
class MyUserManager(BaseUserManager):
use_in_migrations = True
def create_user(self, email, name, mobile, user_type, password):
return create_superuser(self, email, name, mobile, user_type, password)
# python manage.py createsuperuser
def create_superuser(self, email, name, mobile, user_type, password):
user = self.model(
email = email,
name = name,
mobile = mobile,
user_type = user_type,
access_valid_start = utility.now(),
access_valid_end = utility.get_access_end_date(),
created_when = utility.now(),
created_by = constants.COMMAND_LINE_USER_ID,
last_updated_when = utility.now(),
last_updated_by = constants.COMMAND_LINE_USER_ID,
notes = 'This user is created from command line. createsuperuser utility.'
)
user.set_password(password)
user.save(using=self._db)
return user
I also created the Authentication Backend.
class MyAuthBackend(object):
def authenticate(self, email, password):
try:
user = UserModel.objects.get(email=email)
if user.check_password(password):
return user
else:
return None
except UserModel.DoesNotExist:
logging.getLogger("error_logger").error("user with login %s does not exists " % login)
return None
except Exception as e:
logging.getLogger("error_logger").error("user with login %s does not exists " % login)
return None
def get_user(self, user_id):
# user_id must be the primary key of table.
try:
user = UserModel.objects.get(sys_id=user_id)
if user.is_active:
return user
return None
except UserModel.DoesNotExist:
logging.getLogger("error_logger").error("user with %(user_id)d not found")
return None
I have included custom user model and backend, both in settings file.
AUTH_USER_MODEL = 'accounts.UserModel'
AUTHENTICATION_BACKENDS = ('accounts.backends.MyAuthBackend',)
I created a superuser from command line but when I tried to login from admin login url, following error was thrown.
Please enter the correct email and password for a staff account. Note that both fields may be case-sensitive.
As suggest in this SO answer, I used fallback authentication backend and then it started throwing this error : 'UserModel' object has no attribute 'has_module_perms'. Which means fallback backend worked here. But it required to add below functions in custom user model.
def has_perm(self, perm, obj=None):
return self.is_superuser
# this methods are require to login super user from admin panel
def has_module_perms(self, app_label):
return self.is_superuser
and added is_superuser field as well. Now it is working fine.
So I have below questions :
Why I am not able to authenticate user using custom backend when trying to login from admin panel?
why function has_perm, is_staff and is_superuser fields are mandatory for login from admin panel?
Why has_perm function , is_staff and is_superuser fields are not required when logging in from my own login form?
You did authenticate but you are not authorized. Django Admin is using is_staff flag to authorize you in as stated in documentation
They are part of Django simple permission system which is by default used by Django admin as stated in documentation
You are not using permissions system otherwise they would be required as they are part of permission system.