i try to create a custom user model for adding some custom fields to a user. i used the in django 1.5 introduced new method based on AbstractBaseUser. Everything (login) works, except for the admin-panel. When logging into the admin-interface, i get the following error:
AttributeError at /admin/
'ShopUser' object has no attribute 'is_superuser'
here's my model:
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from django.contrib import auth
class ShopUserManager(BaseUserManager):
def create_user(self, email, password=None):
if not email:
raise ValueError("We need an e-mail here...")
user = self.model(
email = ShopUserManager.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(
email,
password = password,
)
user.is_admin = True
user.is_staff = True
user.save(using=self._db)
return user
class ShopUser(AbstractBaseUser):
email = models.EmailField(
verbose_name = 'e-mail address',
max_length = 255,
unique = True,
db_index = True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = ShopUserManager()
USERNAME_FIELD = 'email'
# REQUIRED_FIELDS = ['']
def __unicode__(self):
return self.email
def has_perms(self, perm_list, obj=None):
"""
Returns True if the user has each of the specified permissions. If
object is passed, it checks if the user has all required perms for this
object.
"""
for perm in perm_list:
if not self.has_perm(perm, obj):
return False
return True
def has_module_perms(self, app_label):
"""
Returns True if the user has any permissions in the given app label.
Uses pretty much the same logic as has_perm, above.
"""
# Active superusers have all permissions.
if self.is_active and self.is_superuser:
return True
return _user_has_module_perms(self, app_label)
any advice on this? thanks!
You don't have to let your class inherit from PermissionsMixin.
I had the same problem and I fixed it by adding a few required methods to my user class (in your case ShopUser).
class ShopUser(AbstractBaseUser):
...
def get_full_name(self):
return self.fullname
def get_short_name(self):
return self.shortname
#property
def is_superuser(self):
return self.is_admin
#property
def is_staff(self):
return self.is_admin
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return self.is_admin
Let the class ShopUser inherit from PermissionsMixin like so:
class ShopUser(AbstractBaseUser, PermissionsMixin):
This will add the is_superuser field and should play nicely with the admin UI.
Your create_superuser method should set user.is_superuser = True.
And finally, you should implement get_full_name and get_short_name for your ShopUser class.
More info here: Customizing authentication in Django
Related
I'm extending AbstractBaseUser with my custom user model. I can create a superuser via shell successfully with the UserManager() below which is created in the database correctly.
For testing, I've created a superuser with the username test & password of test.
check_password()
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
def check_password(self, raw_password):
"""
Return a boolean of whether the raw_password was correct. Handles
hashing formats behind the scenes.
"""
def setter(raw_password):
self.set_password(raw_password)
# Password hash upgrades shouldn't be considered password changes.
self._password = None
self.save(update_fields=["password"])
return check_password(raw_password, self.password, setter)
I can run this test user against the check_password("test", "test") method which returns True as expected, but if I try to login via /admin I get "Password Incorrect" with a 200 status code on the POST.
Update: check_password does return False when given the raw password & hash
>>> u = User.objects.get(pk=1)
>>> u.check_password('test')
False
>>> u.check_password('pbkdf2_sha256$150000$sWSs4Yj3gQe1$75A2JmFurNX2oOeKJ18TvsB2G3YU6mYjIuHlaH7i6/k=')
False
Relevant app versions
Django==2.2.3
djangorestframework==3.10.1
User Model
class User(AbstractBaseUser):
USERNAME_FIELD = ('username')
REQUIRED_FIELDS = ('email', 'password')
username = models.CharField(max_length=15, unique=True)
twitch_id = models.IntegerField(null=True)
avatar = models.URLField(null=True, blank=True)
is_live = models.BooleanField(default=False)
email = models.EmailField(unique=True)
password = models.CharField(max_length=50, default="password")
register_date = models.DateTimeField(auto_now=True)
twitch_token = models.ForeignKey(TwitchToken, on_delete=models.SET_NULL, null=True)
twitter_token = models.ForeignKey(TwitterToken, on_delete=models.SET_NULL, null=True)
# attempted giving flags from original User model
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
objects = UserManager()
class Meta:
db_table = 'users_user'
def __str__(self):
return self.username
"""
Properties are redundant with flags above
#property
def is_admin(self):
return self.admin
#property
def is_active(self):
return self.active
#property
def is_staff(self):
return self.staff
#property
def is_superuser(self):
return self.superuser
"""
UserManager()
class UserManager(BaseUserManager):
def create_user(self, username, email, password):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, username, email, password):
"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, username, email, password):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.is_staff = True
user.is_admin = True
user.is_active = True
user.save(using=self._db)
return user
I am explicitly stating to use django.contrib.auth.backends.ModelBackend (default) in my settings & my AUTH_USER_MODEL is set. (I have seen some use a tuple & others use a list. I've tried both, same results)
AUTH_USER_MODEL = 'users.User'
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
)
I suspect I'm not even hitting ModelBackend 'cause I've put some prints in the authenticate() method that aren't running, as well I have deleted the entire file & see the same results. So I suspect the issue is somewhere between Django's determination of the auth user model & actually attempting authentication.
I've looked through countless SO posts & forum posts and I'm not seeing any step of the extension process that I'm missing, but I can't get anything valuable from stack traces either.
Django ModelBackend for Reference
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
UserModel = get_user_model()
class ModelBackend:
"""
Authenticates against settings.AUTH_USER_MODEL.
"""
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
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
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, True, None)
return is_active or is_active is None
def _get_user_permissions(self, user_obj):
return user_obj.user_permissions.all()
def _get_group_permissions(self, user_obj):
user_groups_field = get_user_model()._meta.get_field('groups')
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
return Permission.objects.filter(**{user_groups_query: user_obj})
def _get_permissions(self, user_obj, obj, from_name):
"""
Return the permissions of `user_obj` from `from_name`. `from_name` can
be either "group" or "user" to return permissions from
`_get_group_permissions` or `_get_user_permissions` respectively.
"""
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
perm_cache_name = '_%s_perm_cache' % from_name
if not hasattr(user_obj, perm_cache_name):
if user_obj.is_superuser:
perms = Permission.objects.all()
else:
perms = getattr(self, '_get_%s_permissions' % from_name)(user_obj)
perms = perms.values_list('content_type__app_label', 'codename').order_by()
setattr(user_obj, perm_cache_name, {"%s.%s" % (ct, name) for ct, name in perms})
return getattr(user_obj, perm_cache_name)
def get_user_permissions(self, user_obj, obj=None):
"""
Return a set of permission strings the user `user_obj` has from their
`user_permissions`.
"""
return self._get_permissions(user_obj, obj, 'user')
def get_group_permissions(self, user_obj, obj=None):
"""
Return a set of permission strings the user `user_obj` has from the
groups they belong.
"""
return self._get_permissions(user_obj, obj, 'group')
def get_all_permissions(self, user_obj, obj=None):
if not user_obj.is_active or user_obj.is_anonymous or obj is not None:
return set()
if not hasattr(user_obj, '_perm_cache'):
user_obj._perm_cache = {
*self.get_user_permissions(user_obj),
*self.get_group_permissions(user_obj),
}
return user_obj._perm_cache
def has_perm(self, user_obj, perm, obj=None):
return user_obj.is_active and perm in self.get_all_permissions(user_obj, obj)
def has_module_perms(self, user_obj, app_label):
"""
Return True if user_obj has any permissions in the given app_label.
"""
return user_obj.is_active and any(
perm[:perm.index('.')] == app_label
for perm in self.get_all_permissions(user_obj)
)
def get_user(self, user_id):
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
class AllowAllUsersModelBackend(ModelBackend):
def user_can_authenticate(self, user):
return True
class RemoteUserBackend(ModelBackend):
"""
This backend is to be used in conjunction with the ``RemoteUserMiddleware``
found in the middleware module of this package, and is used when the server
is handling authentication outside of Django.
By default, the ``authenticate`` method creates ``User`` objects for
usernames that don't already exist in the database. Subclasses can disable
this behavior by setting the ``create_unknown_user`` attribute to
``False``.
"""
# Create a User object if not already in the database?
create_unknown_user = True
def authenticate(self, request, remote_user):
"""
The username passed as ``remote_user`` is considered trusted. Return
the ``User`` object with the given username. Create a new ``User``
object if ``create_unknown_user`` is ``True``.
Return None if ``create_unknown_user`` is ``False`` and a ``User``
object with the given username is not found in the database.
"""
if not remote_user:
return
user = None
username = self.clean_username(remote_user)
# Note that this could be accomplished in one try-except clause, but
# instead we use get_or_create when creating unknown users since it has
# built-in safeguards for multiple threads.
if self.create_unknown_user:
user, created = UserModel._default_manager.get_or_create(**{
UserModel.USERNAME_FIELD: username
})
if created:
user = self.configure_user(user)
else:
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
pass
return user if self.user_can_authenticate(user) else None
def clean_username(self, username):
"""
Perform any cleaning on the "username" prior to using it to get or
create the user object. Return the cleaned username.
By default, return the username unchanged.
"""
return username
def configure_user(self, user):
"""
Configure a user after creation and return the updated user.
By default, return the user unmodified.
"""
return user
class AllowAllUsersRemoteUserBackend(RemoteUserBackend):
def user_can_authenticate(self, user):
return True
The ModelBackend.authenticate method first gets the user object from the database using the get_by_natural_key method of the user models default manager, if this fails then authentication will fail
def get_by_natural_key(self, username):
return self.get(**{self.model.USERNAME_FIELD: username})
Because your create_user method is not setting the username field correctly this is failing
The reason why you were able to create the user even though the username field is required is probably because you are using MySQL and running in non-strict mode in which case null values will be converted to empty strings
**Hi
I am trying to customize the django default user. Applying migrations works fine. However I get an error when i try to create a super user. Can you please advise why I get this error?
******************** self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
File "/home/gravityns/PycharmProjects/dev/shop/models.py", line 54, in create_superuser
user.is_staff = True
AttributeError: can't set attribute
# accounts.models.py
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
from django.core.validators import RegexValidator
class UserManager(BaseUserManager):
def create_user(self, username, password, email):
"""
Creates and saves a User with the given email and password.
"""
if not username:
raise ValueError('Users must have a username')
if not email:
raise ValueError('Users must have an email address')
user = self.model(
username = username,
email = self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, username, password, email):
"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.is_staff = True
user.save(using=self._db)
return user
def create_superuser(self, username, password, email):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(
username,
email,
password
)
user.is_staff = True
user.is_admin = True
user.save(using=self._db)
return user
USERNAME_REGEX = '^[a-zA-Z0-9.#+-]*$'
class User(AbstractBaseUser):
username = models.CharField(max_length=255, validators=[
RegexValidator(regex= USERNAME_REGEX,
message = 'Username must be Alphanumeric or any of the following: ". # + -"')],
unique=True
)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
created_at = models.DateField(auto_now_add=True, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
# notice the absence of a "Password field", that's built in.
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email'] # Email & Password are required by default.
objects = UserManager()
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 __str__(self): # __unicode__ on Python 2
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?"
return self.is_staff
#property
def is_admin(self):
"Is the user a admin member?"
return self.is_admin
#property
def is_active(self):
"Is the user active?"
return self.is_active
You're overwriting the normal is_staff attribute with your custom property. You need to rename those properties so that they don't clash with the stuff AbstractBaseUser already implements. Look at how that class implements is_staff and others etc here: https://github.com/django/django/blob/master/django/contrib/auth/models.py#L288
You have a field on User model as User.is_staff and a #property with the same name. So you can't set the property of the model object by user_instance.is_staff = True.
You can have is_staff as a field or as a class property, not both.
You have unncessarily defined properties for is_staff, is_active and is_superuser. But those are already fields, with the exact same name. Therefore, when you try and set user.is_staff = True, Python access your property, and tries to "set" that, rather than the field.
There is no reason to do this. The only reason you would need to define those properties is if you want to disallow setting the fields themselves. But you don't want to do that. (Alternatively, if you didn't want fields at all, but wanted to base the value on some custom logic - but, again, you wouldn't be able to set them unless you defined a custom setter.)
Remove all three of those properties.
My custom user model for login via email:
class MyUser(AbstractBaseUser):
id = models.AutoField(primary_key=True) # AutoField?
is_superuser = models.IntegerField(default=False)
username = models.CharField(unique=True,max_length=30)
first_name = models.CharField(max_length=30, default='')
last_name = models.CharField(max_length=30, default='')
email = models.EmailField(unique=True,max_length=75)
is_staff = models.IntegerField(default=False)
is_active = models.IntegerField(default=False)
date_joined = models.DateTimeField(default=None)
# Use default usermanager
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
class Meta:
db_table = 'auth_user'
For which, I'm able to successfully create a superuser. However, when I try to login with email and password, I get this error:
'MyUser' object has no attribute 'has_module_perms'
Any idea what I'm doing wrong ?
Your User implementation is not providing the mandatory methods to be used with the Admin module.
See https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#custom-users-and-django-contrib-admin.
In your case, add the permissions mixin (PermissionsMixin), as a superclass of your model:
from django.contrib.auth.models import PermissionsMixin
class MyUser(AbstractBaseUser, PermissionsMixin):
# ...
It is described here : https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#custom-users-and-permissions
It works with Django 1.x, 2.x, 3.x and 4.x.
EDIT: updated links to django version 4.0
I think your model is missing some attributes such as 'has_module_params'...Try to add the following
class MyUser(AbstractBaseUser):
...
...
def has_perm(self, perm, obj=None):
return self.is_superuser
def has_module_perms(self, app_label):
return self.is_superuser
For those who are just stepping in November 2017 and after, I don't think adding or allowing your class to inherit PermissionsMixin is the way out, being that it will raise more error since you have reinvented the wheel.
I ran into the same problem this afternoon (4th Nov, 2017) having override Username with Phone Number:
class MyUserManager(BaseUserManager):
..
..
def create_superuser(self, phone, password=None):
if password is None:
raise TypeError("provide password please")
myuser = self.model(phone=phone)
myuser.set_password(password)
myuser.is_admin = True
myuser.is_staff = True
myuser.save()
return myuser
So, http://127.0.0.1:8000/admin/ wasn't working and kept raising object has no attribute 'has_module_perms' error, the following is how I fixed mine:
class MyUser(AbstractBaseUser):
..
..
def get_full_name(self):
pass
def get_short_name(self):
pass
#property
def is_superuser(self):
return self.is_admin
#property
def is_staff(self):
return self.is_admin
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return self.is_admin
#is_staff.setter
def is_staff(self, value):
self._is_staff = value
I hope this helps someone.
I'm trying to create a custom user profile and have modified the example only slightly
from django.conf import settings
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, first_name=None, last_name=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=MyUserManager.normalize_email(email),
first_name=first_name,
last_name=last_name,
#date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password, first_name=None, last_name=None):
user = self.create_user(email,
password=password,
first_name=first_name,
last_name=last_name,
#date_of_birth=date_of_birth
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
db_index=True,
)
first_name=models.CharField(max_length = 30),
last_name=models.CharField(max_length = 30),
#date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
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
When I try to run syncdb I get the following error:
You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Email address: uou#pce.com
Password:
Password (again):
TypeError: 'first_name' is an invalid keyword argument for this function
I have struggled to debug this because of the limited nature of the error message. I feel I'm making a simple mistake what am I doing wrong?
You are trying to set None (Null) value into first_name and it seems that this property don't allow it.
Try this changes:
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, first_name='', last_name=''):
In model:
first_name=models.CharField(max_length = 30, blank = True)
I use django 1.5 with custom model MyUser. I want to make users profile page, where he can modify only one field - 'about'.
I tried something like that:
forms.py:
class UserSettingsForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ('about')
view.py:
class UserSettings(UpdateView):
form_class = UserSettingsForm
template_name = "user/settings.html"
def get_object(self, queryset=None):
return self.request.user
def get_success_url(self):
return reverse('user_detail', args=[self.request.user.username])
urls:
url(r'^settings/$', UserSettings.as_view(), name='user_settings')
model:
class MyUserManager(BaseUserManager):
def create_user(self, email, 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, email, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(email,
password=password,
#date_of_birth=date_of_birth
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
db_index=True,
)
last_name=models.CharField(max_length=30)
first_name=models.CharField(max_length=30)
about=models.TextField(blank=True)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['last_name','first_name','about']
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
But I got error: django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'app.MyUser' that has not been installed
How can I make users profile in django 1.5? Thx!
MyUser class should be under the application named 'app' in your settings file in order for the auth framework to pick it.