Can't create a custom User model in django - python

I have a custom passwordless user model built in django 1.11. user model looks like this
class User(models.Model):
email = models.EmailField(primary_key=True)
REQUIRED_FIELDS = []
USERNAME_FIELD = 'email'
is_anonymous = False
is_authenticated = True
It's a custom user model and depends on a custom auth backend, given like this
class PasswordlessAuthenticationBackend():
def authenticate(self, uid):
try:
token = Token.objects.get(uid=uid)
return User.objects.get(email=token.email)
except User.DoesNotExist:
return User.objects.create(email=token.email)
except Token.DoesNotExist:
return None
def get_user(self, email):
try:
return User.objects.get(email=email)
except User.DoesNotExist:
return None
The auth is registered and working fine. The token is just this
class Token(models.Model):
email = models.EmailField()
uid = models.CharField(default=uuid.uuid4, max_length=40)
The problem is, when I try to call auth.login in my TestCase, it always throws this error:
ValueError: The following fields do not exist in this model or are m2m fields: last_login
What are m2m fields? How and where do I specify this last_login?
Edit:
The failing test looks like this:
from django.test import TestCase
from django.contrib.auth import get_user_model
from django.contrib import auth
from accounts.models import Token
User = get_user_model()
email = 'test#testing.com'
class UserModelTestcase(TestCase):
def test_user_is_valid_with_email_only(self):
user = User(email=email)
user.full_clean()
def test_email_is_primary_key(self):
user = User(email=email)
self.assertEqual(user.pk,email)
def test_links_user_with_auto_generated_uid(self):
token1 = Token.objects.create(email=email)
token2 = Token.objects.create(email=email)
self.assertNotEqual(token1.uid, token2.uid)
def test_no_problem_with_auth_login(self):
user = User.objects.create(email=email)
user.backend = ''
request = self.client.request().wsgi_request
auth.login(request, user) #should not raise, fails!

this link Error about Django custom authentication and login?
has an in-depth explanation about your problem an has multiple solutions you can pick.

Related

Django login/authentication system not really working

I'm a newbie in django and I'm having some problems, what I'm trying to do is a simple login system in Django with a custom backend and using postgresql as the main db.
The problem is, my authentication and login function is apparently working normally but the user is not actually logged in, I wrote a custom message to let me know when the user is logged in and my index is protected against anonymous user, so I basically can't access.
This code from my views.py
#login_required(login_url='signin')
def index(request):
return render(request, 'index.html')
def signin(request):
now = datetime.now()
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
print(username, password)
user = UserBackend.authenticate(UserBackend(), username=username, password=password)
if user is not None:
login(request, user, backend='core.backends.UserBackend')
print('Logged In') #### This print/checking is working fine!
return redirect('/')
#return render(request, 'index.html')
else:
messages.info(request, 'Invalid Username or Password! Try again')
return redirect("signin")
#else:
return render(request,'signin.html')
#user = auth.authenticate(username=username, password=password)
return render(request, 'signin.html')
This is my user class from models.py
class user(AbstractBaseUser):
id = models.AutoField(primary_key=True)
username = models.TextField()
real_name = models.TextField()
email = models.TextField()
password = models.TextField()
description = models.TextField()
last_login = models.DateField()
created_at = models.DateField()
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
class Meta:
managed = False
db_table = 'user'
def __str__(self) -> str:
return self.username
And my user backend from backends.py
class UserBackend(ModelBackend):
def authenticate(self, **kwargs):
username = kwargs['username']
password = kwargs['password']
print('user: ', username)
print('pass: ', password)
#try:
user_ = user.objects.get(username=username)
try:
print(user_.check_password(password))
if user_.check_password(password) is True:
return user_
except user.DoesNotExist:
pass
def get_user(self, user_id):
try:
return user.objects.get(pk=user_id)
except user.DoesNotExist:
return None
I tried changing the return on views.py to something like
return redirect(request.GET.get('next'))
but still not working :(
what should I do?
The key point, is how you are using your Backend. Although, lets start from the beginning...
Starting with your models, you are unnecessarily overwriting a lot of fields. Django's AbstractUser has these fields you are creating. In fact, the model contains the following fields with validators where appropriate:
username
first_name
last_name
email
is_staff as default false
is_active as default true
date_joined as default timezone.now()
last_login
a built-in function get_full_name (equivalent to your real_name field)
Also, id = models.AutoField(primary_key=True) is not necessary Django by default creates this kind of field automatically for every model. Most of the times used when you want a different kind of field as PK. Thus a simple model, would fulfill your requirements:
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
description = models.TextField()
The backend is pretty straight forward, check credentials and return an User object instance if everything is correct. ModelBackend already has a get_user method defined.
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
User = get_user_model()
class UserBackend(ModelBackend):
def authenticate(self, request, **kwargs):
username = kwargs['username']
password = kwargs['password']
try:
user = User.objects.get(username=username)
pwd_valid = check_password(password, user.password)
if pwd_valid:
return user
else:
return None
except User.DoesNotExist:
return None
Now, about on how to use it. Quotting the documentation:
Behind the scenes, Django maintains a list of “authentication
backends” that it checks for authentication. When somebody calls
django.contrib.auth.authenticate() Django tries authenticating across
all of its authentication backends. If the first authentication method
fails, Django tries the second one, and so on, until all backends have
been attempted.
The list of authentication backends to use is specified in the
AUTHENTICATION_BACKENDS setting. This should be a list of Python path
names that point to Python classes that know how to authenticate.
These classes can be anywhere on your Python path.
So, first we need to add AUTHENTICATION_BACKENDS in settings.py:
AUTH_USER_MODEL = 'core.User'
LOGIN_URL = '/signin'
AUTHENTICATION_BACKENDS = [
'core.auth.backends.UserBackend',
'django.contrib.auth.backends.ModelBackend'
]
And lastly in views.py, we call the functions exactly as you would in a normal login function:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, authenticate
from django.contrib import messages
# Create your views here.
#login_required
def index(request):
return render(request, 'index.html')
def signin(request):
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Confirm that backend is UserBackend
print(user.backend)
return redirect('core:index')
else:
messages.info(request, 'Invalid Username or Password! Try again')
return redirect("core:signin")
return render(request, 'signin.html')

Django: user.has_perm always returns false

I have a custom user:
from django.contrib.auth.models import AbstractUser
# Create your models here.
class TaborUser(AbstractUser):
email = models.EmailField('E-mail', unique=True)
Its backend:
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
UserModel = get_user_model()
class EmailBackend(BaseBackend):
def get_user(self, user_id):
user = UserModel.objects.filter(pk=user_id)
breakpoint()
if user:
return user[0]
else:
return None
def authenticate(self, request, username=None, password=None, **kwargs):
user = UserModel.objects.filter(email=username)
if not user:
user = UserModel.objects.filter(username=username)
# Both username and e-mail are unique. As long as we don't have
# a very rogue admin, we should be alright.
if user:
user = user[0]
else:
return None
if user.check_password(password):
return user
else:
return None
The model does not seem to pass this check:
class AdminView(PermissionRequiredMixin, FormView):
form_class = UploadFileForm
template_name = "admin.html"
login_url = "/login/"
permission_required = ("taborapp.view_photomodel",
"taborapp.add_photomodel",
"taborapp.delete_photomodel",
)
When user is added as follows:
from taborapp.models import TaborUser
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
user = TaborUser.objects.create_user("test")
user.email = "test#example.me"
user.set_password("LOLaPublicPassword123")
permissions = []
photo_type = ContentType.objects.get(app_label="taborapp", model="photomodel")
for codename in "view_photomodel", "add_photomodel", "delete_photomodel":
perm = Permission.objects.filter(content_type=photo_type, codename=codename)
permissions.append(perm[0])
user.user_permissions.add(*permissions)
user.save()
Am I doing anything wrong? I went over docs and similar issues on stack overflow a few times and I just cannot figure out the issue.
I am too brief for the auto-detection system to believe me that I have described my problem enough. I hope this sentence will satisfy it.
Drop into the Django shell:
python manage.py shell
Next,
from django.contrib.auth.models import User
u = User.objects.get(email="test#example.me")
u.get_user_permissions()
Hopefully you'll see what you're looking for in the data returned. That'll give you a good idea of where to look next.
As pointed out by #RiverRook, getting the list of permissions was very helpful as I managed to find the correct thread about this.
Django user get_all_permissions() is empty while user_permissions is set

How To Change Default Fields In Django Login Form [duplicate]

I want django to authenticate users via email, not via usernames. One way can be providing email value as username value, but I dont want that. Reason being, I've a url /profile/<username>/, hence I cannot have a url /profile/abcd#gmail.com/.
Another reason being that all emails are unique, but it happen sometimes that the username is already being taken. Hence I'm auto-creating the username as fullName_ID.
How can I just change let Django authenticate with email?
This is how I create a user.
username = `abcd28`
user_email = `abcd#gmail.com`
user = User.objects.create_user(username, user_email, user_pass)
This is how I login.
email = request.POST['email']
password = request.POST['password']
username = User.objects.get(email=email.lower()).username
user = authenticate(username=username, password=password)
login(request, user)
Is there any other of of login apart from getting the username first?
You should write a custom authentication backend. Something like this will work:
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
Then, set that backend as your auth backend in your settings:
AUTHENTICATION_BACKENDS = ['path.to.auth.module.EmailBackend']
Updated. Inherit from ModelBackend as it implements methods like get_user() already.
See docs here: https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#writing-an-authentication-backend
If you’re starting a new project, django highly recommended you to set up a custom user model. (see https://docs.djangoproject.com/en/dev/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project)
and if you did it, add three lines to your user model:
class MyUser(AbstractUser):
USERNAME_FIELD = 'email'
email = models.EmailField(_('email address'), unique=True) # changes email to unique and blank to false
REQUIRED_FIELDS = [] # removes email from REQUIRED_FIELDS
Then authenticate(email=email, password=password) works, while authenticate(username=username, password=password) stops working.
Email authentication for Django 3.x
For using email/username and password for authentication instead of the default username and password authentication, we need to override two methods of ModelBackend class: authenticate() and get_user():
The get_user method takes a user_id – which could be a username, database ID or whatever, but has to be unique to your user object – and returns a user object or None. If you have not kept email as a unique key, you will have to take care of multiple result returned for the query_set. In the below code, this has been taken care of by returning the first user from the returned list.
from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try: #to allow authentication through phone number or any other field, modify the below statement
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
UserModel().set_password(password)
except MultipleObjectsReturned:
return User.objects.filter(email=username).order_by('id').first()
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
def get_user(self, user_id):
try:
user = UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
By default, AUTHENTICATION_BACKENDS is set to:
['django.contrib.auth.backends.ModelBackend']
In settings.py file, add following at the bottom to override the default:
AUTHENTICATION_BACKENDS = ('appname.filename.EmailBackend',)
Django 4.0
There are two main ways you can implement email authentication, taking note of the following:
emails should not be unique on a user model to mitigate misspellings and malicious use.
emails should only be used for authentication if they are verified (as in we have sent a verification email and they have clicked the verify link).
We should only send emails to verified email addresses.
Custom User Model
A custom user model is recommended when starting a new project as changing mid project can be tricky.
We will add an email_verified field to restrict email authentication to users with a verified email address.
# app.models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
email_verified = models.BooleanField(default=False)
We will then create a custom authentication backend that will substitute a given email address for a username.
This backend will work with authentication forms that explicitly set an email field as well as those setting a username field.
# app.backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
UserModel = get_user_model()
class CustomUserModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
if username is None or password is None:
return
try:
user = UserModel._default_manager.get(
Q(username__exact=username) | (Q(email__iexact=username) & Q(email_verified=True))
)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
We then modify our projects settings.py to use our custom user model and authentication backend.
# project.settings.py
AUTH_USER_MODEL = "app.User"
AUTHENTICATION_BACKENDS = ["app.backends.CustomUserModelBackend"]
Be sure that you run manage.py makemigrations before you migrate and that the first migration contains these settings.
Extended User Model
While less performant than a custom User model (requires a secondary query), it may be better to extend the existing User model in an existing project and may be preferred depending on login flow and verification process.
We create a one-to-one relation from EmailVerification to whichever User model our project is using through the AUTH_USER_MODEL setting.
# app.models.py
from django.conf import settings
from django.db import models
class EmailVerification(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_query_name="verification"
)
verified = models.BooleanField(default=False)
We can also create a custom admin that includes our extension inline.
# app.admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from .models import EmailVerification
UserModel = get_user_model()
class VerificationInline(admin.StackedInline):
model = EmailVerification
can_delete = False
verbose_name_plural = 'verification'
class UserAdmin(BaseUserAdmin):
inlines = (VerificationInline,)
admin.site.unregister(UserModel)
admin.site.register(UserModel, UserAdmin)
We then create a backend similar to the one above that simply checks the related models verified field.
# app.backends.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
UserModel = get_user_model()
class ExtendedUserModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD, kwargs.get(UserModel.EMAIL_FIELD))
if username is None or password is None:
return
try:
user = UserModel._default_manager.get(
Q(username__exact=username) | (Q(email__iexact=username) & Q(verification__verified=True))
)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
We then modify our projects settings.py to use our authentication backend.
# project.settings.py
AUTHENTICATION_BACKENDS = ["app.backends.ExtendedUserModelBackend"]
You can then makemigrations and migrate to add functionality to an existing project.
Notes
if usernames are case insensitive change Q(username__exact=username) to Q(username__iexact=username).
In production prevent a new user registering with an existing verified email address.
I had a similar requirement where either username/email should work for the username field.In case someone is looking for the authentication backend way of doing this,check out the following working code.You can change the queryset if you desire only the email.
from django.contrib.auth import get_user_model # gets the user_model django default or your own custom
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
# Class to permit the athentication using email or username
class CustomBackend(ModelBackend): # requires to define two functions authenticate and get_user
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
# below line gives query set,you can change the queryset as per your requirement
user = UserModel.objects.filter(
Q(username__iexact=username) |
Q(email__iexact=username)
).distinct()
except UserModel.DoesNotExist:
return None
if user.exists():
''' get the user object from the underlying query set,
there will only be one object since username and email
should be unique fields in your models.'''
user_obj = user.first()
if user_obj.check_password(password):
return user_obj
return None
else:
return None
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
Also add AUTHENTICATION_BACKENDS = ( 'path.to.CustomBackend', ) in settings.py
Email and Username Authentication for Django 2.X
Having in mind that this is a common question, here's a custom implementation mimicking the Django source code but that authenticates the user with either username or email, case-insensitively, keeping the timing attack protection and not authenticating inactive users.
from django.contrib.auth.backends import ModelBackend, UserModel
from django.db.models import Q
class CustomBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
def get_user(self, user_id):
try:
user = UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
Always remember to add it your settings.py the correct Authentication Backend.
It seems that the method of doing this has been updated with Django 3.0.
A working method for me has been:
authentication.py # <-- I placed this in an app (did not work in the project folder alongside settings.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class EmailBackend(BaseBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(email=username)
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
Then added this to the settings.py file
AUTHENTICATION_BACKENDS = (
'appname.authentication.EmailBackend',
)
I have created a helper for that: function authenticate_user(email, password).
from django.contrib.auth.models import User
def authenticate_user(email, password):
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
class LoginView(View):
template_name = 'myapp/login.html'
def get(self, request):
return render(request, self.template_name)
def post(self, request):
email = request.POST['email']
password = request.POST['password']
user = authenticate_user(email, password)
context = {}
if user is not None:
if user.is_active:
login(request, user)
return redirect(self.request.GET.get('next', '/'))
else:
context['error_message'] = "user is not active"
else:
context['error_message'] = "email or password not correct"
return render(request, self.template_name, context)
Authentication with Email and Username For Django 2.x
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
class EmailorUsernameModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username))
except UserModel.DoesNotExist:
return None
else:
if user.check_password(password):
return user
return None
In settings.py, add following line,
AUTHENTICATION_BACKENDS = ['appname.filename.EmailorUsernameModelBackend']
You should customize ModelBackend class.
My simple code:
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
class YourBackend(ModelBackend):
def authenticate(self, username=None, password=None, **kwargs):
UserModel = get_user_model()
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
if '#' in username:
UserModel.USERNAME_FIELD = 'email'
else:
UserModel.USERNAME_FIELD = 'username'
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
And in settings.py file, add:
AUTHENTICATION_BACKENDS = ['path.to.class.YourBackend']
from django.contrib.auth.models import User
from django.db import Q
class EmailAuthenticate(object):
def authenticate(self, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(email=username) | Q(username=username))
except User.DoesNotExist:
return None
except MultipleObjectsReturned:
return User.objects.filter(email=username).order_by('id').first()
if user.check_password(password):
return user
return None
def get_user(self,user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
And then in settings.py:
AUTHENTICATION_BACKENDS = (
'articles.backends.EmailAuthenticate',
)
where articles is my django-app, backends.py is the python file inside my app and EmailAuthenticate is the authentication backend class inside my backends.py file
May, 2022 Update:
This instruction shows how to set up authentication with "email" and "password" instead of "username" and "password" and in this instruction, "username" is removed and I tried not to change the default Django settings as much as possible.
First, run the command below to create "account" application:
python manage.py startapp account
Then, set "account" application to "INSTALLED_APPS" and set AUTH_USER_MODEL = 'account.CustomUser' in "settings.py" as shown below:
# "settings.py"
INSTALLED_APPS = [
# ...
"account", # Here
]
AUTH_USER_MODEL = 'account.CustomUser' # Here
Then, create "managers.py" just under "account" folder and create "CustomUserManager" class extending "UserManager" class in "managers.py" as shown below. *Just copy & paste the code below to "managers.py" and "managers.py" is necessary to make the command "python manage.py createsuperuser" work properly without any error:
# "account/managers.py"
from django.contrib.auth.models import UserManager
from django.contrib.auth.hashers import make_password
class CustomUserManager(UserManager): # Here
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError("The given email must be set")
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", False)
extra_fields.setdefault("is_superuser", False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email=None, password=None, **extra_fields):
extra_fields.setdefault("is_staff", True)
extra_fields.setdefault("is_superuser", True)
if extra_fields.get("is_staff") is not True:
raise ValueError("Superuser must have is_staff=True.")
if extra_fields.get("is_superuser") is not True:
raise ValueError("Superuser must have is_superuser=True.")
return self._create_user(email, password, **extra_fields)
Then, create "CustomUser" class extending "AbstractUser" class and remove "username" by setting it "None" and set "email" with "unique=True" and set "email" to "USERNAME_FIELD" and set "CustomUserManager" class to "objects" in "account/models.py" as shown below. *Just copy & paste the code below to "account/models.py":
# "account/models.py"
from django.db import models
from django.contrib.auth.models import AbstractUser
from .managers import CustomUserManager
class CustomUser(AbstractUser):
username = None # Here
email = models.EmailField('email address', unique=True) # Here
USERNAME_FIELD = 'email' # Here
REQUIRED_FIELDS = []
objects = CustomUserManager() # Here
class Meta:
verbose_name = "custom user"
verbose_name_plural = "custom users"
Or, you can also create "CustomUser" class extending "AbstractBaseUser" and "PermissionsMixin" classes as shown below. *This code below with "AbstractBaseUser" and "PermissionsMixin" classes is equivalent to the code above with "AbstractUser" class and as you can see, the code above with "AbstractUser" class is much less code:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.utils import timezone
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
first_name = models.CharField("first name", max_length=150, blank=True)
last_name = models.CharField("last name", max_length=150, blank=True)
email = models.EmailField('email address', unique=True)
is_staff = models.BooleanField(
"staff status",
default=False,
help_text="Designates whether the user can log into this admin site.",
)
is_active = models.BooleanField(
"active",
default=True,
help_text=
"Designates whether this user should be treated as active. "
"Unselect this instead of deleting accounts."
,
)
date_joined = models.DateTimeField("date joined", default=timezone.now)
USERNAME_FIELD = 'email'
objects = CustomUserManager() # Here
Then, create "forms.py" just under "account" folder and create "CustomUserCreationForm" class extending "UserCreationForm" class and "CustomUserChangeForm" class extending "UserChangeForm" class and set "CustomUser" class to "model" in "Meta" inner class in "CustomUserCreationForm" and "CustomUserChangeForm" classes in "account/forms.py" as shown below. *Just copy & paste the code below to "forms.py":
# "account/forms.py"
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('email',)
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('email',)
Then, create "CustomUserAdmin" class extending "UserAdmin" class and set "CustomUserCreationForm" and "CustomUserChangeForm" classes to "add_form" and "form" respectively and set "AdminPasswordChangeForm" class to "change_password_form" then set "CustomUser" and "CustomUserAdmin" classes to "admin.site.register()" in "account/admin.py" as shown below. *Just copy & paste the code below to "account/admin.py":
from django.contrib import admin
from .forms import CustomUserCreationForm, CustomUserChangeForm
from django.contrib.auth.forms import AdminPasswordChangeForm
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
fieldsets = (
(None, {"fields": ("password",)}),
("Personal info", {"fields": ("first_name", "last_name", "email")}),
(
"Permissions",
{
"fields": (
"is_active",
"is_staff",
"is_superuser",
"groups",
"user_permissions",
),
},
),
("Important dates", {"fields": ("last_login", "date_joined")}),
)
add_fieldsets = (
(
None,
{
"classes": ("wide",),
"fields": ("email", "password1", "password2"),
},
),
)
add_form = CustomUserCreationForm # Here
form = CustomUserChangeForm # Here
change_password_form = AdminPasswordChangeForm # Here
list_display = ("email", "first_name", "last_name", "is_staff")
list_filter = ("is_staff", "is_superuser", "is_active", "groups")
search_fields = ("first_name", "last_name", "email")
ordering = ("email",)
filter_horizontal = (
"groups",
"user_permissions",
)
admin.site.register(CustomUser, CustomUserAdmin) # Here
Then, run the command below to make migrations and migrate:
python manage.py makemigrations && python manage.py migrate
Then, run the command below to create a superuser:
python manage.py createsuperuser
Then, run the command below to run a server:
python manage.py runserver 0.0.0.0:8000
Then, open the url below:
http://localhost:8000/admin/login/
Finally, you can log in with "email" and "password" as shown below:
And this is "Add custom user" page as shown below:
For Django 2
username = get_object_or_404(User, email=data["email"]).username
user = authenticate(
request,
username = username,
password = data["password"]
)
login(request, user)
Authentication with Email For Django 2.x
def admin_login(request):
if request.method == "POST":
email = request.POST.get('email', None)
password = request.POST.get('password', None)
try:
get_user_name = CustomUser.objects.get(email=email)
user_logged_in =authenticate(username=get_user_name,password=password)
if user_logged_in is not None:
login(request, user_logged_in)
messages.success(request, f"WelcomeBack{user_logged_in.username}")
return HttpResponseRedirect(reverse('backend'))
else:
messages.error(request, 'Invalid Credentials')
return HttpResponseRedirect(reverse('admin_login'))
except:
messages.warning(request, 'Wrong Email')
return HttpResponseRedirect(reverse('admin_login'))
else:
if request.user.is_authenticated:
return HttpResponseRedirect(reverse('backend'))
return render(request, 'login_panel/login.html')
If You created Custom database, from there if you want to validate your email id and password.
Fetch the Email id and Password with models.objects.value_list('db_columnname').filter(db_emailname=textbox email)
2.assign in list fetched object_query_list
3.Convert List to String
Ex :
Take the Html Email_id and Password Values in Views.py
u_email = request.POST.get('uemail')
u_pass = request.POST.get('upass')
Fetch the Email id and password from the database
Email = B_Reg.objects.values_list('B_Email',flat=True).filter(B_Email=u_email)
Password = B_Reg.objects.values_list('Password',flat=True).filter(B_Email=u_email)
Take the Email id and password values in the list from the Query value set
Email_Value = Email[0]
Password_Value=Password[0]
Convert list to String
string_email = ''.join(map(str, Email_Value))
string_password = ''.join(map(str, Password_Value))
Finally your Login Condition
if (string_email==u_email and string_password ==u_pass)
The default user model inherits/ Extends an Abstract class. The framework should be lenient to a certain amount of changes or alterations.
A simpler hack is to do the following:
This is in a virtual environment
Go to your django installation location and find the Lib folder
navigate to django/contrib/auth/
find and open the models.py file. Find the AbstractUser class line 315
LINE 336 on the email attribute add unique and set it to true
email = models.EmailField(_('email address'), blank=True,unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
Done, makemigrations & migrate
Do this at you own risk,
Pretty simple. There is no need for any additional classes.
When you create and update a user with an email, just set the username field with the email.
That way when you authenticate the username field will be the same value of the email.
The code:
# Create
User.objects.create_user(username=post_data['email'] etc...)
# Update
user.username = post_data['email']
user.save()
# When you authenticate
user = authenticate(username=post_data['email'], password=password)

'user' not available in template django

I am using a custom user model with builtin and custom authentication backends. the default for username-password login and the custom one for uid based login The login is working fine but the 'user' (Other than superuser) is not available in the template.Nor is the last_login getting updated in database. Here is my code
backend.py
from support.models import CustomUser
class UsernameIdModelBackend(object):
def authenticate(self, request, uid=None):
try:
user= CustomUser.objects.get(uid=uid)
return user
except CustomUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return CustomUser.objects.get(pk=user_id)
except CustomUser.DoesNotExist:
return None
models.py
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
uid = models.CharField(max_length=50)
Where did i go wrong..
EDIT :
I was using if user.is_authenticated before the 'user'. After removing the if condition i am seeing AnonymousUser.

Authentication login with legacy database in Django

I am creating a project in Django and I have a HelpDesk database that I would like to use in the project. In the database I have the table log_user where the logins information is stored, but I can not find a way to authenticate other than the Django default table.
Views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import Person
from django.contrib.auth import authenticate, login
def user_login(request):
if request.method == 'POST':
form = Person(request.POST)
if form.is_valid():
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return HttpResponse('Authenticated sucessfully')
else:
return HttpResponse('Disable account')
else:
return HttpResponse('Invalid Login')
else:
form = Person()
return render(request, 'login2.html', {'form': form})
forms.py
from django import forms
from .models import Loginteste
class Person(forms.ModelForm):
class Meta:
model = Loginteste
fields = ['username', 'password']
models.py
from django.db import models
class Loginteste(models.Model):
username = models.CharField(max_length=50, blank=True, null=True)
password = models.CharField(max_length=50, blank=True, null=True)
class Meta:
managed = True
db_table = 'loginteste'
auth_backend.py
from .models import Loginteste
class MyBackend:
def authenticate(self, request, username=None, password=None):
user = Loginteste.object.get(username=username)
if user.check_password(password):
return (request, user)
I tried with the default table "User" and the login function worked, but I can not do it with an already existing table (log_user in database HelpDesk).
I think I understand your problem now.
As far as I know, Django is really dependant on the User model; it can be a custom user model, but there has to be some sort of User model.
When you build your own authentication backend, it still has to return User instances (or your custom User model instances).
For example:
File auth_backends.py
from django.contrib.auth.models import User
from .models import Loginteste
class MyAuthBackend:
def authenticate(self, request, username=None, password=None):
if username is None or password is None:
# nothing to do
return None
# get 'User' object
try:
usr = User.objects.get(username=username)
except User.DoesNotExist:
return None
# get 'Loginteste' object
try:
loginteste = Loginteste.objects.get(username=username)
except Loginteste.DoesNotExist:
return None
# authenticate user
if not loginteste.check_password(password):
# incorrect password
return None
return usr
def get_user(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
pass
return None
File settings.py
AUTHENTICATION_BACKENDS = [
'project_config.auth_backends.MyAuthBackend',
# 'django.contrib.auth.backends.ModelBackend',
]
The only relatively simple option you might have is to use the model Loginteste as your custom user model. Everything else involves overriding a lot of Djangos code to make it work.

Categories

Resources