Custom user model not working on django admin - python

I created an app authentication, code for models is,
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
# username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
apart from this the changes that i did was added authentication in INSTALLED_APPS and in settings added AUTH_USER_MODEL = 'authentication.Account'
When i create a super-user from shell, its working and inserting in db, but when i try to login into the django admin console,
initially the error was is_staff not found, which i added. Now it is showing invalid username and password for all. I tried that a couple of time generating new one each time. What am i doing wrong ?
EDIT
I am adding the dbs dump that i used to validate if the user was created
sqlite> select * from authentication_account;
1|pbkdf2_sha256$24000$ZRHCXpUI3w0G$fh4z9y5vmYAZ0FEiDr908dJD3ezw62nm3lpkZxUi/Es=||me#rahulbhola.in|||1|0|2016-05-28 21:15:59.292988|2016-05-28 21:15:59.415382
Still login in django defaut admin does not work

You added the is_staff later on which seems to be false by default, so when you migrated the field was created in database with the value false in the is_staff field.
Please make the changes as below and try
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
account = self.model(email=self.normalize_email(email))
account.set_password(password)
account.save(using=self.db)
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_staff = True
account.is_admin = True
account.save(using=self.db)
return account
Please not account.save(using=self.db) in the code above and account.is_staff = True in the create admin method.
Please try creating the new user again i prefer using the shell ( python manage.py shell)
from authentication.models import Account
user = Account()
user.create_superuser(username='some_username', password='some_password')
user.save()
Now that the user is created please try logging in using the django admin.
This should work. I had similar issues which is fixed and i am able to create user and login from django admin.
I am assuming that you have created the custom authentication backend already, if not please create
Hope this helps you.

Related

Python - Create multiple user types and email as the username field in Django 2

I'm working on a project using Python(3.7) and Django(2.2) in which I have to implement multiple types of users as:
Personal Account - below 18
Personal Account - above 18
Parent Account
Coach Account
Admin
along with that, I also need to use email as the username field for login/authentication.
The strategy I'm trying to use is to build a custom base model as User inherited from AbstractBaseUser and also created a custom User Manager to make the email as username but it's not working.
Here's my complete model code:
class UserManager(BaseUserManager):
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
if not email:
raise ValueError('Users must have an email address')
now = timezone.now()
email = self.normalize_email(email)
user = self.model(
email=email,
is_staff=is_staff,
is_active=True,
is_superuser=is_superuser,
last_login=now,
date_joined=now,
**extra_fields
)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
return self._create_user(email, password, False, False, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
user = self._create_user(email, password, True, True, **extra_fields)
user.save(using=self._db)
return user
def generate_cid():
customer_number = "".join([random.choice(string.digits) for i in range(10)])
return customer_number
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
is_personal_above_18 = models.BooleanField(default=False)
is_personal_below_18 = models.BooleanField(default=False)
is_parent = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def get_absolute_url(self):
return "/users/%i/" % self.pk
def get_email(self):
return self.email
class PersonalAccountAbove18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(default=generate_cid)
class PersonalAccountBelow18(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(blank=False)
class ParentAccount(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
primary_key=True, related_name='profile')
customer_id = models.BigIntegerField(default=generate_cid)
I'm confused about my approach and even it's also return an error when I run makemigrations as:
users.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
Update:
I removed the PermissionMixin and related_name attributes from child models and migrations are running now but it still require the username instead of email.
The errors in makemigrations, and later (after dropping PermissionsMixin) requiring username instead of email for authentication are hints that you have not set your custom model as the default user model to be used by the Django auth app. As a result, Django is using the auth.User model as the default user model and your one being added to the project like any other model. So both of the user models exist simultaneously with the auth.User being the default/active one used for authentication purposes.
In essence, edit your settings.py to add the following:
AUTH_USER_MODEL = '<your_app>.User'
Now Django will use your customized User model instead of the one from auth app (the default) and the auth.User model will be dropped. The auth app uses the get_user_model (django.contrib.auth.get_user_model) function to get the currently active user model for the project which checks for settings.AUTH_USER_MODEL, and by default the rest of the system (e.g. the admin app) also checks for this setting to get the current user model. So as long as you're using the auth app the above should suffice.
For using email as login, I recommend the django-allauth package. It takes care of all the heavy lifting and you can use it to login with email instead of username. Will Vincent has a write up on this at:
https://wsvincent.com/django-allauth-tutorial-custom-user-model/
Will also has a good write up on creating custom user models at:
https://wsvincent.com/django-custom-user-model-tutorial/
In short, he recommends subclassing AbstractUser. Here is an example from one of my projects where a user collects points throughout their journey on the site and I must record these points.
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
points = models.IntegerField(default=0)
def user_id(self):
return self.id.__str__()

Login to Admin in Django

I'd appreciate help with a custom user modul. I set it up analog to this example in the django documentation. I have the following problems:
Login to the admin interface does not work. The login page shows up, but does not accept my credentials after creating an user with ./manage createsuperuser in the shell.
When creating a superuser, it saves initially the password in cleartext. I had a look in the database and found the password in clear text. I guess this comes from create_superuser() where user.set_password() is not used but password=password (as in the example from django docs, so why would they do that?). I changed it in the shell and then it is encrypted. Login still doesnt work tho.
My Code is as following:
authentication/models.py
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
**kwargs
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **kwargs):
user = self.model(
email,
password=password,
**kwargs
)
user.is_admin=True
user.save(using=self._db)
return user
and
class MyUser(AbstractBaseUser):
# use this for auth and sessions
# REQUIRED_FIELDS = ['password']
session = models.ForeignKey(
'sessions.Session',
verbose_name='Session',
blank=True, null=True,
)
email = models.EmailField(unique=True, primary_key=True)
#password = forms.CharField(max_length=30, widget=forms.PasswordInput()) #render_value=False
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
signed_up_since = models.DateTimeField('Signed up since', default=timezone.now())
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
I edited the settings:
AUTH_USER_MODEL = "authentication.MyUser"
I dont use a custom session backend or authentication backends, did the migrations, sqlmigrations etc. Shell gives me this:
>>> user.is_staff
True
Any ideas? Thanks a lot!
The problem is that you are creating a user using self.model() in your create_user method. This leads to the password being saved as plain text.
You should use the create_user method, as in the example in the docs. This will hash the password correctly.
def create_superuser(self, email, password, **kwargs):
user = self.create_user(
email,
password=password,
**kwargs
)

Best practice for email required in django-rest-auth

What is the best (easiest?) way to make it a requirement to have an email only for user registration and not make username a requirement in the django-rest-auth framework?
Would I need to write a new User Serializer, for example? Is there a flag setting that I can set True or False to turn off or on this requirement instead?
Thanks
You'll need to write your own AbstractUser and BaseUserManager classes. Fortunately it's pretty simple, add something like this to your app's models:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True, blank=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
# This tells Django that this field is absolutely important...
USERNAME_FIELD = 'email'
# ...and username is now optional because it doesn't show up here!
REQUIRED_FIELDS = []
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
class AccountManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError('User must provide an e-mail address')
account = self.model(
email=self.normalize_email(email)
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password=None):
account = self.create_user(email, password)
account.is_admin = True
account.save()
return account
Next, tell Django that this model is your project's new User class. Add the following to your settings.py file:
AUTH_USER_MODEL = 'apiapp.Account'
Once that's done, just migrate:
python manage.py makemigrations
python manage.py migrate
From that point on new users will be represented by this new model (you'd have to handle migrations manually), including anywhere you might use self.request.user!

How to custom user models in django 1.5

As we all know, django provide an strong User and Authenticate app.
as usually, there is two way to custom the user
use OneToOneField way . we can define a model profile which keep some user information fields. we also can use get_profile method to get the profile. but,there is a problem, if
we get the user profile, we must join the data table. some code demo:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User)
use AbstractBaseUser, PermissionsMixin . we can custom the user by change the AbstractBaseUser, PermissionsMixin in django--1.5, and must define the UserAdmin.the document:https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#extending-the-existing-user-model
I am very confused and do not know which way is better?
You have to do is add AUTH_USER_MODEL to settings with path to custom user class, which extends either AbstractBaseUser (more customizable version) or AbstractUser.
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=MyUserManager.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, date_of_birth, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
u = self.create_user(username,
password=password,
date_of_birth=date_of_birth
)
u.is_admin = True
u.save(using=self._db)
return u
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
def get_full_name(self):
# The user is identified by their email address
return self.email
def get_short_name(self):
# The user is identified by their email address
return self.email
def __unicode__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
The best way to do this now since Django 1.5 is with custom user models, setting AUTH_USER_MODEL to your user model.
There's no reason to be confused. The docs you link to are correct: you should extend AbstractBaseUser.
Also, you should upgrade to version 1.6.

Abstract base manager isn't accessible

I'm trying to create a new user, but I get this message:
_get_
raise AttributeError("Manager isn't accessible via %s instances" % type._name_)
AttributeError: Manager isn't accessible via CompanyUser instances
This is what I'm doing:
def obj_create(self, bundle, request=None, **kwargs):
try:
bundle = super(AccountCreateResource, self).obj_create(bundle)
bundle.obj.set_password(bundle.data.get('password'))
bundle.obj.objects.create_user()
except IntegrityError:
raise BadRequest('Username already exists')
I should beable to access the manager. This is my model:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
class EmailUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves an EmailUser with the given email and password.
"""
now = timezone.now()
if not email:
raise ValueError('The given email must be set')
email = EmailUserManager.normalize_email(email)
user = self.model(email=email, is_staff=False, is_active=True,
is_superuser=False, last_login=now,
date_joined=now, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, **extra_fields):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(email, password, **extra_fields)
user.is_staff = True
user.is_active = True
user.is_superuser = True
user.save(using=self._db)
return user
class AbstractEmailUser(AbstractBaseUser, PermissionsMixin):
"""
Abstract User with the same behaviour as Django's default User but
without a username field. Uses email as the USERNAME_FIELD for
authentication.
Use this if you need to extend EmailUser.
Inherits from both the AbstractBaseUser and PermissionMixin.
The following attributes are inherited from the superclasses:
* password
* last_login
* is_superuser
"""
email = models.EmailField(_('email address'), max_length=255,
unique=True, db_index=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)
objects = EmailUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
abstract = True
def get_full_name(self):
"""
Returns the email.
"""
return self.email
def get_short_name(self):
"""
Returns the email.
"""
return self.email
def email_user(self, subject, message, from_email=None):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email])
class CompanyUser(AbstractEmailUser):
"""
Concrete class of AbstractEmailUser.
"""
company = models.CharField(max_length=100)
pass
You don't show enough of the code or the traceback to understand what's really going on, but anyway: the error message you get means you tried to access the Manager from an instance, and it's not allowed, period - the fact that you have an abstract model is totally irrelevant. You have to access the Manager from the model class itself. Hopefully it's just a matter of replacing instance.objects by type(instance).objects.
It's really not clear what you are trying to do here. This line - the one that's causing the error - makes no sense:
bundle.obj.objects.create_user()
Since you haven't provided the code for bundle, I have to guess that this is a model with an obj ForeignKey, which points to CompanyUser (rather strange naming conventions, though). But as the error says, you can't call objects on an instance, only on the class. But even if you could, the line still doesn't make sense: obj is already a User, so why would you want to create one? And again, create_user takes at least an email parameter, which you're not supplying.

Categories

Resources