I'm making a login for my app, with a custom user model and form, using an e-mail as username field in the model, and asking for email and password during the login process. The problem is that no matches are found in the authenticate method in my view, so I can't log in my users. Here is the code:
user model
class usuarios(AbstractBaseUser):
nombre = models.CharField(max_length=32, null=False)
apellido = models.CharField(max_length=32, null=False)
correo = models.EmailField(null=False, unique=True)
password = models.CharField(max_length=200, null=False)
fecha_nacimiento = models.DateField(null=False)
USERNAME_FIELD = 'correo'
objects = userManager()
def __str__(self):
return self.correo+' '+self.nombre+' '+self.apellido
login form
class loginForm(forms.Form):
correo = forms.EmailField()
password = forms.CharField(
widget=forms.PasswordInput()
)
class Meta:
model = usuarios
fields = ("correo","password")
login view
class usuario_login(FormView):
template_name = "user/usuario_login.html"
form_class = loginForm
success_url = reverse_lazy('user:crear')
def form_valid(self, form):
usuario = authenticate(
username=form.cleaned_data['correo'],
password=form.cleaned_data['password']
)
if usuario is not None:
login(self.request, usuario)
else:
return HttpResponseRedirect(
reverse_lazy('user:login')
)
return super(usuario_login, self).form_valid(form)
As you can see, in the login view I try to authenticate username (that's the email) and password before doing the login. I appreciate any help and then you.
Nevermind, the problem was that as I changed the AbstractBaseUser, I had to change the authentication user model too, so I just added this in settings as the documentation states:
AUTH_USER_MODEL = 'user.usuarios'
Related
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"
]
i created a custom form from the Django-allauth package adding a first name, Lastname and is_staff. This was after i had extended the user model using abstart user. i think it has something to do with is_staff being a boolean value. All suggestions are welcome. thank you.
My user model looks like this
from django.contrib.auth.models import AbstractUser, UserManager
class CustomUserManager(UserManager):
def get_by_natural_key(self, username):
case_insensitive_username_field = '{}__iexact'.format(self.model.USERNAME_FIELD)
return self.get(**{case_insensitive_username_field: username})
class User(AbstractUser):
is_student = models.BooleanField('student status', default=False)
location = models.CharField(max_length=30, blank=True)
# first_name= models.CharField( blank=False, max_length=30, verbose_name='first name')
email = models.EmailField(unique=True)
objects = CustomUserManager()
My custom form is as shown below
from django.contrib.auth.models import User, Group
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='First Name')
last_name = forms.CharField(max_length=30, label='Last Name')
is_staff = forms.BooleanField(required=True ,label = 'Are you been assigned as a staff?')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
Update your signup method and add the following line before save,
user.is_staff = self.cleaned_data['is_staff']
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'm new to Django, and I've been fighting with this form for over a week now. I started this first because it is the core of my project. What I ultimately want is a single form that has a condition that the user can set, to add data to a second form when they check a radio box (will do that in jquery). I want to give admins the ability to register users, but I have a special subclass of users called operators that need additional information in a separate model. I almost have it working right now, but all users get added to the special subclass. Please help!
EDIT: What's not working is that I want to have the Admins register a user on a single form that can create users and if they check a button, then fill out the rest of the form for operators. I have the second part working (they can create an operator), but I also want them be able to create a regular user with the same form (that doesn't use the operator model). Can this be done?
Here is my code. NOTE: I messed up the password registration in this code, but I'll fix that later. Just working on this core functionality right now.
Models
class UserProfile(AbstractUser):
bio = models.TextField(max_length=500, blank=True)
birth_date = models.DateField(null=True, blank=True)
profile_pic = models.ImageField(null=True, blank=True)
notes = models.TextField(null=True, blank=True)
def __str__(self):
return self.first_name + ' ' + self.last_name
class OperatorProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
cdl = models.ManyToManyField('CDL', blank=True)
endorsement = models.ManyToManyField('Endorsement', blank=True)
cdl_expiration = models.DateField(blank=True, null=True)
def __str__(self):
return str(self.user)
Views
class OperatorCreateView(CreateView):
model = OperatorProfile
template_name = 'pages/operatorprofile_form.html'
form_class = UserCreationMultiForm
success_url = reverse_lazy('index')
def form_valid(self, form):
# Save the user first, because the profile needs a user before it
# can be saved.
user = form['user'].save()
user.groups.add(Group.objects.get(name='Operators'))
profile = form['profile'].save(commit=False)
profile.user = user
profile.save()
form['profile'].save_m2m()
return redirect(reverse_lazy('index'))
Forms
# Operator Creation Form
class OperatorProfileForm(forms.ModelForm):
class Meta:
model = OperatorProfile
exclude = ['user']
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
username = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
password_confirm = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
email = forms.CharField(widget=forms.EmailInput(attrs={'class': 'form-control'}))
bio = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control'}))
def clean_password2(self):
password = self.cleaned_data.get('password1')
password_confirm = self.cleaned_data.get('password2')
if not password_confirm:
raise forms.ValidationError("You must confirm your password")
if password != password_confirm:
raise forms.ValidationError("Your passwords do not match")
return password_confirm
class Meta:
model = UserProfile
fields = ['username', 'password', 'password_confirm', 'first_name', 'last_name', 'bio']
class UserCreationMultiForm(MultiModelForm):
form_classes = {
'user': UserProfileForm,
'profile': OperatorProfileForm,
}
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.