I have a custom user model that extends an AbstractBaseUser, as well as my own user manager (GenericUserManager):
class GenericUserManager(BaseUserManager):
def create_user(self, username, email, password, key_expires):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError("users must have a username")
if not password:
raise ValueError('users must have password')
user = self.model(
username=username,
email=self.normalize_email(email),
key_expires=key_expires,
)
user.set_password(password)
user.save(using=self._db)
return user
def get_by_natural_key(self, username):
return self.get(username=username)
class BaseRegistrationUser(AbstractBaseUser):
username = models.CharField(max_length=150, unique=True)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=False,
)
activation_key = models.CharField(max_length=90)
key_expires = models.DateTimeField()
is_active = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
date_joined = models.DateTimeField(('date joined'),
default=datetime.datetime.now)
is_merchant_or_customer = models.CharField(max_length=20, null=False)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username, email']
def __str__(self):
return self.username
...
def natural_key(self):
return (self.email,)
...
class Customer(BaseRegistrationUser):
first_name = models.CharField(max_length=150, default="", null=True)
last_name = models.CharField(max_length=150, default="", null=True)
slug = models.SlugField(max_length=175, default="", null=True)
objects = GenericUserManager()
...
These work fine for registration purposes, however, whenever I attempt to log a 'customer' user I get the following error:
File "C:\Users\OEM\Documents\repos\repo\ecommerce\myapp\views.py" in get_profile
126. user = authenticate(username=username, password=password)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\__init__.py" in authenticate
70. user = _authenticate_with_backend(backend, backend_path,
request, credentials)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\__init__.py" in _authenticate_with_backend
115. return backend.authenticate(*args, **credentials)
File "C:\Users\OEM\AppData\Local\Programs\Python\Python36-32\lib\site-
packages\django\contrib\auth\backends.py" in authenticate
18. user =
UserModel._default_manager.get_by_natural_key(username)
Exception Type: AttributeError at /get-profile/
Exception Value: 'Manager' object has no attribute 'get_by_natural_key'
I don't know what causes this since the 'get_by_natural_key' attribute is defined in my UserManager.
I've tried new database migrations and spent a solid 3 days just combing through other questions on this site, but unfortunately, it hasn't helped. I would be extremely grateful someone could help me figure this out.
EDIT:Fixed the indenting, but the error persists
It looks like your indentation is wrong. get_by_natural_key() is a function, not a method of your manager class.
Ok, so the solution, in hindsight, should have been more obvious. I correctly assigned the customer my user manager class, however, I had not applied the same to my BaseRegistrationUser, which meant that it was missing the required methods to log in using the django.auth methods
Related
I am working on my Django project, I am getting this error but i don't really know how to fix it exactly, i have created my custom user model and the user manager
class AccountManager(BaseManager):
def create_user(self, email, fullname=None, birthday=None,zipcode=None, password=None):
if not email:
raise ValueError('Users must have an email address')
user = self.model(Email_Address=self.normalize_email(email),
name=self.normalize_email(email),
Date_of_Birth=birthday,
zipcode=zipcode
)
user.set_password(password)
user.save(using='self._db')
return user
def create_superuser(self, Email_Address, username, password):
user=self.create_user(Email_Address=self.normalize_email(Email_Address), password=password,)
user.is_admin = True
user.is_active = True
user.is_staff = True
user.is_superuser = True
user.save(using='self._db')
class User(AbstractUser):
Email_Address = models.EmailField(verbose_name='email', unique=True, blank=True, null=True)
Date_of_Birth = models.CharField(max_length=30, blank=True, null=True, default=None)
name = models.CharField(max_length=30, blank=True, null=True)
username= models.CharField(max_length=30,unique=True, blank=True, null=True)
zipcode = models.CharField(max_length=30, blank=True, null=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
USERNAME_FIELD = 'Email_Address'
objects = AccountManager()
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
because i am using rest_framework_simplejwt for authentication i am getting this error when i try to log in
user = self.user_model.objects.get(**{api_settings.USER_ID_FIELD: user_id})
rest_framework.request.WrappedAttributeError: 'AccountManager' object has no attribute 'get'
can anyone please help me with this
According to this section, https://docs.djangoproject.com/en/4.0/topics/auth/customizing/#writing-a-manager-for-a-custom-user-model, you need to extend your custom user manager from BaseUserManager.
In your code you extended from the BaseManager.
I am using AbstractBaseUser for that i have created a managers.py for creating superuser.
Everything is working except these line in managers.py. Even i tried to print(user.id) before user.save(using=self._db) it is printing None
user.save(using=self._db)
user.created_by_id = user.id
user.updated_by_id = user.id
user.save()
I have also used commit=False in user.save(using=self._db, commit=False) still it is giving error. See the errors at bottom.
In my models.py i have tweo field created_by & updated_by which should should not be null. Thats why i am saving in Managers.py which is creating all this error.
How can i solve this issue. Is there better approach. This condition is only required for creating superuser.
Models.py
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
is_active = models.BooleanField(_('is user active'), default=True)
profile_image = models.ImageField(default='user-avatar.png', upload_to='users/', null=True, blank=True)
is_staff = models.BooleanField(_('staff'), default=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=30, blank=True, null=True)
address = models.CharField(max_length=100, blank=True, null=True)
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_created_by')
created_on = models.DateTimeField(_('user created on'), auto_now_add=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_updated_by')
updated_on = models.DateTimeField(_('user updated on'), auto_now=True)
Managers.py
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.role_id = 1
user.first_name = "Super"
user.last_name = "Admin"
user.save(using=self._db)
user.created_by_id = user.id
user.updated_by_id = user.id
user.save()
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_superuser', 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)
Error - for commit=False
super().save(*args, **kwargs)
TypeError: save() got an unexpected keyword argument 'commit'
Error - Without Commit False
raise utils.IntegrityError(*tuple(e.args))
django.db.utils.IntegrityError: (1048, "Column 'created_by_id' cannot be null")
commit=False works for ModelForms. It does not work for model instances. As the error said in your question, there is no keyword argument named commit exists in save method.
Also, before saving the user object, it can't produce any primary key. I would recommend to make created_by null able. So that you can avoid integrity error.
created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_created_by', null=True, default=None)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, related_name='user_updated_by', null=True, default=None)
Still, if you want to enforce this restriction, then you can do that in forms or serializer or override the save method as well in the User model.
For example:
class User(...):
...
def save(self, *args, **kwargs):
if not self.is_superuser:
if not self.created_by or not self.updated_by:
raise Exception("Add these fields") # can be custom exception
super(User, self).save(*args, **kwargs)
I just started a new Django project and I wanted to create my own customer user model instead of using the default. I pretty much followed the one in the documentation for 2.1 (my version of Django) except I added more fields and date_of_birth is called 'dob' and isn't required. But then when I create the superuser account, I can't log into the admin panel. It says, "Please enter the correct email address and password for a staff account. Note that both fields may be case-sensitive."
Here is my code:
users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.utils.translation import gettext_lazy as _
# Create your models here.
class UserManager(BaseUserManager):
# python manage.py createuser
def create_user(self, email: str, password: str=None) -> 'User':
if not email:
raise ValueError('Users must have a valid email address.')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
# python manage.py createsuperuser
def create_superuser(self, email, password) -> 'User':
user = self.create_user(
email=email,
password=password,
)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(
_('email address'), help_text=_('An email address'), max_length=127, unique=True, null=False, blank=False
)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
first_name = models.CharField(_('first name'), help_text=_('The user\'s given name'), blank=False, max_length=32)
last_name = models.CharField(
_('last name'), help_text=_('The user\'s surname or family name'), blank=False, max_length=32
)
dob = models.DateField(_('date of birth'), help_text=_('User\'s date of birth'), blank=False, null=True)
hometown = models.CharField(_('hometown'), help_text=_('Hometown'), blank=True, max_length=64)
country = models.CharField(_('country'), help_text=_('Country'), blank=True, max_length=64)
objects = UserManager()
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
def __str__(self):
return self.email
def get_full_name(self):
return self.first_name + " " + self.last_name
def get_short_name(self):
return self.first_name
def has_perm(self, perm, obj=None):
return self.is_staff
def has_module_perms(self, app_label):
return self.is_staff
#property
def is_staff(self) -> bool:
"""
Is the user a member of staff?
:return: If the user is an admin, then True. Else False
"""
return self.is_admin
I also changed my settings.py file to read:
AUTH_USER_MODEL = 'users.User'
and I made sure the app ('myproject.users.apps.UsersConfig') is added to the INSTALLED_APPS list. Note: my apps are all located one folder in, unlike the tutorial default. Ie:
myproject/
|-----> users/
|----->models.py
I think you missed one thing in User model,
Change is_active = models.BooleanField(default=False) to is_active = models.BooleanField(default=True)
Hence your model be like,
class User(AbstractBaseUser):
is_active = models.BooleanField(default=True)
# other fields
From the official doc,
is_active
Boolean. Designates whether this user account should be considered
active. We recommend that you set this flag to False instead of
deleting accounts; that way, if your applications have any foreign
keys to users, the foreign keys won’t break.
I created an user app MyUser which is an extension of AbstractBaseUser. I was under the impression that the model MyUser will replace the standard Auth.User, as long as it is mentioned in settings.py
AUTH_PROFILE_MODULE = 'profile.MyUser'
My trouble is now that I can't set the permissions for users registered in the MyUser model. When I try to set the group memberships and permissions, I get the error User' instance expected, got <MyUser: username>.
How can I add users of my user model to the permissions and correct groups?
class MyUserManager(BaseUserManager):
def create_user(self, username, email, phone, password=None, company=False):
if not email:
raise ValueError('Users must have an email address')
if not username: username = email.split('#')[0]
user = self.model(
email=MyUserManager.normalize_email(email),
username=username,phone=phone,)
user.set_password(password)
user.save(using=self._db)
# add to user group and set permissions
if company:
g = Group.objects.get(name='company')
p = Permission.objects.get(codename='add_company')
else:
g = Group.objects.get(name='user')
p = Permission.objects.get(codename='add_user')
g.user_set.add(user)
user.user_permissions.add(p)
return user
class MyUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True, db_index=True)
phone = models.CharField(_('Phone Number'), max_length=25, blank=True, null=True,)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['phone']
objects = MyUserManager()
def get_full_name(self):
return self.email
...
Just add PermissionsMixin to your model declaration! :)
class MyUser(AbstractBaseUser, PermissionsMixin):
...
Here is the relevant part of Django docs.
You've probably resolved your issue but for completeness did you mean to refer to setting AUTH_USER_MODEL vs AUTH_PROFILE_MODULE when creating a custom user model in Django 1.5.
ref: https://docs.djangoproject.com/en/1.5/topics/auth/customizing/#substituting-a-custom-user-model
Am using a custom user model to maintain user details with django 1.5, below is my code. When using social auth, everything seems to be working fine accept first_name and last_name.
In my table table structure, I do not have first_name and last_name, instead, I have one field full name.
but, i did create a method in the manager to accept first and last names and combine them. but, still am receiving empty values, any advise on why this is happening?
class UserManager(BaseUserManager):
"""
UserManager()
"""
def create_user(self, username, email=None, password=None, first_name='', last_name='', **extra_fields):
if not username:
raise ValueError('Users must have a username')
user = self.model(
username=username,
email=UserManager.normalize_email(email),
full_name='%s %s' % (first_name, last_name),
)
user.set_password(password)
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""
Accounts/User
"""
GENDERS = (
(u'M', _('Male')),
(u'F', _('Female')),
)
username = models.CharField(_('username'), max_length=30, unique=True,
help_text=_('Required. 30 characters or fewer. Letters, numbers and '
'#/./+/-/_ characters'),
validators=[
validators.RegexValidator(re.compile('^[\w.#+-]+$'), _('Enter a valid username.'), 'invalid')
])
email = models.EmailField(_('email address'), blank=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)
# user details and information
full_name = models.CharField(_('full name'), max_length=30, )
date_of_birth = models.DateField(_('birthday'), null=True, blank=True)
bio = models.TextField(_('biography / about you'), null=True, blank=True, help_text=_('Biography / About you'))
gender = models.CharField(_('gender'), max_length=1, blank=True, null=True, choices=GENDERS)
# follows / followers
followings = models.ManyToManyField('self', through='Relationship', symmetrical=False, related_name='followers')
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', 'date_of_birth', 'full_name',]
Django-social-auth doesn't work like that when creating user, instead when calling create_user in your manager, it just passes the username and the email as you can see here. Later in the pipeline more fields in your user model are update when this is called, but as the code says, it does a getattr(user, name, None) which in you case for first_name and last_name, returns None since those fields aren't defined in your model.
You can trick the system by defining some properties in your model, something like this:
class User(...):
#property
def first_name(self):
if not hasattr(self, '_first_name'):
self._first_name = self.full_name.split(' ', 1)[0]
return self._first_name
#first_name.setter
def first_name(self, value):
self._first_name = value
self.build_full_name()
#property
def last_name(self):
if not hasattr(self, '_last_name'):
self._last_name = self.full_name.split(' ', 1)[-1]
return self._last_name
#first_name.setter
def last_name(self, value):
self._last_name = value
self.build_full_name()
def build_full_name(self):
self.full_name = ' '.join([self._first_name, self._last_name])