Django CustomUser redefine password field - python

Suddenly I had the need to use one of my existing models for authentication. I know that there are several approaches how I can do that, but here I'm wondering is it possible to redefine password field with a way I choosed?
To be more specific: I want django to know that my access_key field is a User password field.
But current code presupposes that I gonna add another field password to Company model.
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
from django.db import models
from django_extensions.db.fields.encrypted import EncryptedCharField
class CompanyManager(BaseUserManager):
def create_user(self, name, email, access_key=None):
if not email:
raise ValueError('Company must have an email address')
company = self.model(
email=CompanyManager.normalize_email(email),
)
if access_key is not None:
company.access_key = access_key
company.save(using=self._db)
return company
def create_superuser(self, name, email, access_key):
company = self.create_user(name, email, access_key)
company.is_admin = True
company.save()
return company
class Company(AbstractBaseUser):
name = models.CharField(max_length=64, blank=False, null=False,
unique=True)
email = models.CharField(max_length=254, blank=False, null=False,
unique=True)
access_key = EncryptedCharField(max_length=18)
verification_key = EncryptedCharField(max_length=20, blank=True,
null=True)
is_confirmed = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
callback_url = models.URLField(blank=False, null=True)
objects = CompanyManager()
USERNAME_FIELD = 'name'
REQUIRED_FIELDS = ['email']

Related

How do i add custom fields to UserManager in Django

I am trying to create a user profile, i followed through a tutorial which has registration for only username, email and password but i want to be able to add other custom fields.
What i did:
Models.py:
class UserManager(BaseUserManager):
def create_user(self, username, email, password=None,):
if username is None:
raise TypeError('User should have a userame')
if email is None:
raise TypeError('Users should have a Email')
user = self.model(username=username , email = self.normalize_email(email))
user.set_password(password)
user.save()
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError('User should have a password')
user=self.create_user(username,email,password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(models.Model):
dso = models.ForeignKey(Dso,related_name='dso',default=NULL,blank=False,on_delete=models.CASCADE)
name = models.CharField(max_length=70, blank=False, default='')
email = models.EmailField(max_length=70, blank=False, default='')
password = models.CharField(max_length=70, blank=False, default='')
address = models.CharField(max_length=70, blank=False, default='')
roleId = models.IntegerField(blank=False, default='1')
isActive = models.BooleanField(blank=False, default=True)
customerId = models.CharField(max_length=70, blank=False, default='')
dateJoined = models.DateTimeField(auto_now_add=False, blank=False, default=NULL)
#property
def energy_data(self):
energydata = EnergyData.objects.filter(customerId=self.customerId).first()
return energydata
Serializers.py:
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length = 68, min_length=6, write_only = True)
class Meta:
model=User
fields=['email','username','password','name','address','customerId',
'dso', 'roleId']
def validate(self, attrs):
email = attrs.get('email', '')
username = attrs.get('username', '')
if not len(username) >= 4:
raise serializers.ValidationError('Username must be morethan 4 letters or characters')
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
Views.py:
class RegisterView(generics.GenericAPIView):
serializer_class= RegisterSerializer
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
user= User.objects.get(email=user_data['email'])
token = RefreshToken.for_user(user).access_token
current_site = get_current_site(request).domain
relativeLink = reverse('email-verify')
absolute_url = 'http://'+current_site+relativeLink+"?token="+str(token)
email_body= 'Hi '+ user.username + ' Use this link below to verify your email \n'+ absolute_url
data = {'email_subject': 'Verify Your Email', 'email_body': email_body , 'to_email': user.email}
Util.send_email(data)
return Response(user_data, status = status.HTTP_201_CREATED)
URL Path:
path('register/', RegisterView.as_view(), name="register" )
When i do this and try to test i get the error, 'UserManager.create_user() got an unexpected keyword argument 'name''
Please kindly help as i am new to django rest frameworrk.
In your serializers.py you have the fields list that includes the variable 'name', but it is never defined in the models.py
Try to change serializers.py
fields=['email','username','password','address','customerId',
'dso', 'roleId']
And modify the variable name in models.py to be username instead of name
username = models.CharField(max_length=70, blank=False, default='')
Based on my experience with Django having a Model called "user" is going to create problems at some point since Django already have a User model pre-installed in the backend.
I know this is not the exact answer you were looking for, this will probably spare you a headache in the future.
To create a user profile I created the following model linking the User model with a OneToOneField.
class Profile(models.Model):
user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
bio = models.TextField()
profile_pic = models.ImageField(null=True, blank=True,upload_to="images/")
def __str__(self):
return str(self.user)
and obvioulsy imported
from django.contrib.auth.models import User
As a result,
I would remove your User model
Import "from django.contrib.auth.models import User"
add to the first line of your
UserManager Model, I would add
"user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)

Extending Custom User Model - Multiple User Types in Django

I'm relatively new(er) to django but very excited to learn such a versatile framework. I'm working on a project where I will have 2 user types, account 1 and account 2. Account 2 will have the ability to "add a user" to their account. Think of account 2 as a company who can add users to their company.
So far I've extended the generic User model and have created a class for each of the account types but I'm not sure if I'm doing it correctly. Ultimately I will want to create a login/register form for each of the account types - similar to how ziprecruiter functions so some advice on how to approach that would be awesome too if possible.
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
def __str__(self):
return self.email
class account1(User):
profile = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+', null=True)
# account 2 fields here
first_name = models.TextField(max_length=30, blank=True)
last_name = models.TextField(max_length=30, blank=True)
location = models.TextField(max_length=30, blank=True)
class Meta:
db_table = 'account1_user'
class account2(User):
profile = models.ForeignKey(User, on_delete=models.CASCADE, related_name='+')
# account2 user fields here
class Meta:
db_table = 'account2_user'
Am I doing this correctly? What's the best approach to creating a login/registration form for each type of account? What's the best approach to creating a model that will allow for a user of a user type (if that makes sense)?
Rule of thumb is no matter what your business logic, always use one User model for your application. if you want multiple types of user then you could use attributes to determine user types. For example, in my application i want three types of user, Admin,Broker and Client. Here is how we can do this.
class UserManager(BaseUserManager):
def create_client(self, username, first_name, last_name, email, phone, password=None):
user = self.model(
username=username,
first_name=first_name,
last_name=last_name,
phone=phone,
email=self.normalize_email(email)
)
user.set_password(password)
user.is_client = True
user.save(using=self.db)
return user
def create_reseller(self, username, first_name, last_name, email, phone, password=None):
user = self.create_client(username, first_name, last_name, email, phone, password)
user.is_reseller = True
user.save()
return user
def create_admin(self, username, first_name, last_name, email, phone, password=None):
user = self.create_reseller(username, first_name, last_name, email, phone, password)
user.is_admin = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
is_superuser = None
REQUIRED_FIELDS = ["username", "first_name", "last_name", "phone"]
EMAIL_FIELD = "email"
USERNAME_FIELD = 'email'
objects = UserManager()
email = models.EmailField(unique=True)
username = models.CharField(max_length=DefaultModel.MAX_LENGTH)
first_name = models.CharField(max_length=DefaultModel.MAX_LENGTH)
last_name = models.CharField(max_length=DefaultModel.MAX_LENGTH)
phone = PhoneNumberField(unique=True)
is_admin = models.BooleanField(default=False)
is_reseller = models.BooleanField(default=False)
is_client = models.BooleanField(default=False)
# storing timestamps for users.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
added_by = models.ForeignKey("self", models.CASCADE, default=None, null=True)
So what i did, i'm using custom UserManager Class which is responsible for generating User Model objects and i've implemented methods for generating for all types of users Client, Broker and Admin.
DefaultModel is actually class which i'm using as constants for setting default values for models. Here is what it looks like:
class DefaultModel:
MAX_LENGTH = 1000
MYSQL_UNIQUE_LENGTH = 255
If you don't know about objects in models you may learn about it from Django docs.
Custom User Models
Django Managers

AttributeError: type object 'MyUser' has no attribute 'USERNAME_FIELD'

I am building a custom User class in django to use in creating a signup application and I keep on getting the error above every time I try to makemigrations. As far as I can see, my code is per django documentation here..
I also have AUTH_USER_MODEL correctly placed in my settings configurations.
Here's my models.py
`class MyUserManager(BaseUserManager):
def create_user(self, email,
first_name,last_name,profile_picture,phone_no,password=None):
"""
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),
first_name=first_name,
last_name=last_name,
profile_picture=profile_picture,
phone_no=phone_no,
)
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 and password.
"""
SuperUser = self.create_user(
email,
password=password,
)
SuperUser.staff = True
SuperUser.admin = True
SuperUser.save(using=self._db)
return SuperUser
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name = 'email_address',
max_length=255,
unique=True,
# validators=email_validator,
)
first_name = models.CharField(max_length=20,blank=False,null=False)
last_name = models.CharField(max_length=20,blank=False,null=False)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number
must be entered in the format: '+254 ...'")
phone_no = models.CharField(validators=[phone_regex], max_length=17,
blank=False)
profile_picture = models.ImageField(upload_to='media/',blank=False)
# email_validator = EmailValidator(message='Invalid email
# address',code=None,whitelist=None)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name','last_name','phone_no','profile_picture']
# Email & Password are required by default
def get_full_name(self):
return self.email
def get_short_name():
return self.email
def __str__(self):
return self.email
def has_perm(self,perm,obj=None):
#does user have a specific permission
return True
def has_module_pers(self,app_label):
#does user have permissions to view the app 'app_label'
return True
#property
def is_admin(self):
return self.is_admin
#property
def is_active(self):
return self.is_active
# hook in the New Manager to our Model
class MyUser(AbstractBaseUser):
...
objects = MyUserManager()
`
TO create custom User Model
class User(AbstractUser):
"""User model."""
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
Official Documentation for CustomUser
You are:
- Extending the base class that Django has for User models.
Removing the username field.
Making the email field required and unique.
List itemTelling Django that you are going to use the email field as the
USERNAME_FIELD
Removing the email field from the REQUIRED_FIELDS
settings (it is automatically included as USERNAME_FIELD)
Source Link
USERNAME_FIELD must be UNIQUE when you create custom User model
So you can just make unique Email field like Roshan says, but some cases Email can not be unique for some services.
So I prefer just make one uuid field and make it USERNAME_FIELD like this
class User(AbstractBaseUser):
uid = models.UUIDField(
default=None,
blank=True,
null=True,
unique=True,
)
USERNAME_FIELD = "uid"
Your code has a multiline comment that is ill-formatted
#email_validator = EmailValidator(message='Invalid email
address',code=None,whitelist=None)
the second line of that comment is not actually commented so the single quote after address is opening a string that appears to contain the rest of the class and is closed at the stray single quote at the very end.

No module named base_user - Django

In one of my models files in my Django project, I am trying to import AbstractBaseUser, and BaseUserManager from django.contrib.auth.base_user. I checked my site packages, and there is definitely a base_user python file in Django's auth directory, but I am getting this error when trying to make migrations.
ImportError: No module named base_user
If I was using the wrong Django version, it wouldn't show up in my site packages Django directory correct ? Also in Pycharm, my IDE, its not underlining it red with any errors.
models.py
from django.conf import settings
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.contrib.auth.hashers import get_hasher
from django.contrib.auth.models import PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.text import slugify
class AccountUserManager(BaseUserManager):
# username is not used here, but is needed for facebook login to work correctly
def create_user(self, email, password=None, username=None, is_active=True):
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
)
user.set_password(password)
user.last_login = timezone.now()
user.language_code = settings.LANGUAGE_CODE
user.is_active = is_active
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email,
password=password,
)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class Account(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, max_length=255, verbose_name='Email',
help_text='Used for login and password recovery. Is also an account\'s display name if no Name is specified.')
first_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='First Name',
help_text='User\'s first name.')
last_name = models.CharField(max_length=100, blank=True, null=True, verbose_name='Last Name',
help_text='User\'s last name.')
date_joined = models.DateTimeField(auto_now_add=True, verbose_name='Date Joined')
is_active = models.BooleanField(default=True, null=False, db_index=True, verbose_name='Is Active',
help_text='Uncheck to prevent user from being allowed to login.')
is_staff = models.BooleanField(default=False, null=False, verbose_name='Is Staff',
help_text='Grants administrator privileges.')
activation_key = models.CharField(max_length=40, blank=True, null=True)
key_expires = models.DateTimeField(blank=True, null=True)
objects = AccountUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def get_full_name(self):
full_name = ''
if self.first_name is not None:
full_name += self.first_name
if self.last_name is not None:
full_name += ' ' + self.last_name
return full_name
# #property
def _full_name(self):
return self.get_full_name()
_full_name.short_description = 'Name'
full_name = property(_full_name)
def get_display_name(self):
if self.full_name.strip():
return self.full_name.rstrip()
return self.email.rstrip()
#property
def slug(self):
if self.full_name:
return slugify(self.full_name)
email_parts = self.email.split('#')
return slugify(email_parts[0])
def __unicode__(self):
return unicode(self.email)
class Meta:
verbose_name = 'User Account'
verbose_name_plural = 'User Accounts'

Django not updating SQLite tables properly

I've been working on a project that involves an authentication page using Django and AngularJS. I have created an extended version of the User class and have added "company" and "phone_number" as fields.
Here's my code for models.py:
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
from django.core.validators import RegexValidator
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address')
#if not kwargs.get('username'):
#raise ValueError('Users must have a valid username')
#if access_code not in ['password']:
#raise ValueError('Sorry you are not eligible to join')
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)
first_name = models.CharField(max_length=40, blank=False)
last_name = models.CharField(max_length=40, blank=False)
company = models.CharField(max_length=40, blank=False)
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone_number = models.IntegerField(validators=[phone_regex], blank=False, null=True) # validators should be a list
# access_code = models.CharField(max_length=40, blank=False, default='SOME STRING')
is_admin = 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 = ['first_name', 'last_name', 'company', 'phone_number']
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
Now when I go to terminal and perform python manage.py createsuperuser all the field options pop up for me to enter text. However when I check the database afterwards, only the email and password fields are updated. Company, phone number, first name, and last name return as ' '.
Any clue what I am doing wrong? I've been spending too much time trying to fix this problem.
Thanks
In create_user, you haven't passed any of the other arguments to the self.model call, so you only set the email and, later, the password. You need to pass the kwargs in there too.
account = self.model(email=self.normalize_email(email), **kwargs)

Categories

Resources