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)
Related
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)
Situation:
We have an existing system where current employee logins to the system using their mobile number.
But as same mobile number can be used by multiple users, so there is constraint in existing db that, at any moment there can be only one user with given "Mobile Number" and "is_active: True" (i.e their may be other employees registered with same number earlier, but their status of "is_active" would have changed to "false", when they left).
Also, only admin can add/register new employees/user.
I have created a custom "User" model and "UserManager" as below:
models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.utils.translation import gettext_lazy as _
from .managers import UserManager
# Create your models here.
class User(AbstractBaseUser, PermissionsMixin):
"""
A class implementing a fully featured User model.
Phone number is required. Other fields are optional.
"""
first_name = models.CharField(_('first name'), max_length=50, null=True, blank=True)
last_name = models.CharField(_('last name'), max_length=50, null=True, blank=True)
phone = models.CharField(
_('phone number'),
max_length=10,
null=False,
blank=False,
help_text=_('Must be of 10 digits only')
)
email = models.EmailField(_('email address'), null=True, blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user is a staff member.'),
)
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'), auto_now_add=True)
last_updated = models.DateTimeField(_('last updated'), auto_now=True)
objects = UserManager()
USERNAME_FIELD = 'phone'
REQUIRED_FIELDS = []
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
return self.first_name + ' ' + self.last_name
def get_short_name(self):
"""Return the short name for the user."""
return self.first_name
# def email_user(self, subject, message, from_email=None, **kwargs):
# """Send an email to this user."""
# send_mail(subject, message, from_email, [self.email], **kwargs)
# def sms_user(self, subject, message, from=None, **kwargs):
# """Send a sms to this user."""
# send_sms(subject, message, from, [self.phone], **kwargs)
def __str__(self):
return self.first_name + ' ' + self.last_name
managers.py
from django.contrib.auth.models import BaseUserManager
# Create your models here.
class UserManager(BaseUserManager):
"""Define a model manager for User model"""
use_in_migrations = True
def normalize_phone(self, phone):
"""
Applies NFKC Unicode normalization to usernames so that visually identical
characters with different Unicode code points are considered identical
"""
phone = self.model.normalize_username(phone)
phone = phone.strip()
if not phone.isdigit() or len(phone) != 10:
raise ValueError('Phone number must of 10 digits only')
return phone
def make_default_password(self, phone):
"""
Generates a default password
by concatenating Digi + Phone number
"""
return 'Pass' + phone
def _create_user(self, phone, password=None, **extra_fields):
"""Create and save a User in DB with the given phone"""
if not phone:
raise ValueError('Phone number must be provided')
phone = self.normalize_phone(phone)
password = self.make_default_password(phone)
user = self.model(phone=phone, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, phone, password=None, **extra_fields):
"""Create and save a regular User with the given phone and password"""
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(phone, password, **extra_fields)
def create_staff(self, phone, password=None, **extra_fields):
"""Create and save a regular User with the given phone and password"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', False)
return self._create_user(phone, password, **extra_fields)
def create_superuser(self, phone, password=None, **extra_fields):
"""Create and save a Superuser with the given phone and password"""
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(phone, password, **extra_fields)
Now when I'm running python manage.py makemigrations, its giving following error:
users.User: (auth.E003) 'User.phone' must be unique because it is named as the 'USERNAME_FIELD'
I'm stuck, what to do next.
Any help related to above query or suggestion for how to proceed in the above situation will be highly appreciated.
I am novice to Django and trying to rewrite the rest based backend in Django (using DjangoRestFramework).
Add unique=False on the phone field in your User model.
phone = models.CharField(
_('phone number'),
max_length=10,
unique=False
null=False,
blank=False,
help_text=_('Must be of 10 digits only')
)
Or you can use another field to be the USERNAME_FIELD = ' '. It is set to phone now.
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.
I get the following error after adding the profile_picture field:
TypeError: create_superuser() missing 1 required positional argument: 'profile_picture'
This profile_picture field is an "ImageField" set as "Null = True".
I have tried the following: def create_user(...., profile_picture=None, ....). It didn't work.
and the error occurs only in command prompt when i create superuser from there.
Here is my models.py
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
class UserManager(BaseUserManager):
def create_user(self, email, full_name, profile_picture=None, gender=None, password=None, is_admin=False, is_staff=False, is_active=True):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
if not full_name:
raise ValueError("User must have a full name")
user = self.model(
email=self.normalize_email(email)
)
user.full_name = full_name
user.set_password(password) # change password to hash
# user.profile_picture = profile_picture
user.gender = gender
user.admin = is_admin
user.profile_picture = profile_picture
user.staff = is_staff
user.active = is_active
user.save(using=self._db)
return user
def create_staffuser(self, email, profile_picture, gender, full_name, password=None):
user = self.create_user(
email,
full_name,
profile_picture,
gender,
password=password,
is_staff=True,
)
return user
def create_superuser(self, email, profile_picture, gender, full_name, password=None):
user = self.create_user(
email,
full_name,
profile_picture,
gender,
password=password,
is_staff=True,
is_admin=True,
)
return user
class User(AbstractBaseUser):
username = models.CharField(max_length=255)
full_name = models.CharField(max_length=255)
email = models.EmailField(max_length=255, unique=True,)
profile_picture = models.ImageField(upload_to='user_data/profile_picture', null=True, blank=True)
gender = models.CharField(max_length=255, blank=True, default='rather_not_say')
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
# notice the absence of a "Password field", that's built in.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name', 'gender'] # 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
#staticmethod
def has_perm(perm, obj=None):
# "Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
#staticmethod
def has_module_perms(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.staff
#property
def is_admin(self):
# "Is the user a admin member?"
return self.admin
#property
def is_active(self):
# "Is the user active?"
return self.active
You can add username to the REQUIRED_FIELDS. After that python manage.py createsuperuser asks for username field and it works.
REQUIRED_FIELDS = ['full_name', 'gender', 'username',]
Well, you need to create the create_superuser function as well:
class UserManager(BaseUserManager):
def create_user(self, email, full_name, profile_picture, password=None, is_admin=False, is_staff=False, is_active=True):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
if not full_name:
raise ValueError("User must have a full name")
user = self.model(
email=self.normalize_email(email)
)
user.full_name = full_name
user.set_password(password) # change password to hash
user.profile_picture = profile_picture
user.admin = is_admin
user.staff = is_staff
user.active = is_active
user.save(using=self._db)
return user
def create_superuser(self, email, full_name, profile_picture, password=None, **extra_fields):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
if not full_name:
raise ValueError("User must have a full name")
user = self.model(
email=self.normalize_email(email)
)
user.full_name = full_name
user.set_password(password)
user.profile_picture = profile_picture
user.admin = True
user.staff = True
user.active = True
user.save(using=self._db)
return user
Good Luck!
I had the same problem, it turned out that in the list named REQUIRED_FIELDS was misnamed.
That list tells the django framework to ask for name as well during the creation. Because it is not asking and you've made it necessary.
I hope it helps, best of luck
i solved this problem with some changes
my old code
class User(AbstractUser):
username = None
name = models.CharField(max_length=200, null=True)
email = models.EmailField(unique=True, null=True)
bio = models.TextField(null=True)
avatar = models.ImageField(null=True, default="avatar.svg")
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
and i edited username field to email field
class User(AbstractUser):
username = models.EmailField(unique=True, null=True)
name = models.CharField(max_length=200, null=True)
bio = models.TextField(null=True)
avatar = models.ImageField(null=True, default="avatar.svg")
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
Conculation
username = None => username = models.EmailField(unique=True, null=True)
Happy coding :)
Better to create a class in 0001.initial.py file of migrations. Define a class with all required fields for login and provide dependencies and operations blocks empty.That's it
Make a 0001_initial.py file inside the migrations folder and follow up the below code it will work...
from django.db import migrations
from api.user.models import CustomUser
class Migration(migrations.Migration):
def seed_data(apps, schema_editor):
user = CustomUser(name='name',
email='mail#gmail.com',
is_staff=True,
is_superuser=True,
phone='987654321',
gender='Male'
)
user.set_password('anypassword')
user.save()
dependencies=[
]
operations=[
migrations.RunPython(seed_data),
]
You need to add "profile_picture" to "REQUIRED_FIELDS" in the class "User(AbstractBaseUser)" in "models.py":
# "models.py"
class User(AbstractBaseUser):
# ... # Here
REQUIRED_FIELDS = ['full_name', 'profile_picture', 'gender']
# ...
You need to add profile_picture to REQUIRED_FIELDS in the class User(AbstractBaseUser) in models.py:
I want to create a custom authentication for Django. Created a custom class and manager as well. I'm not able to run the command python manage.py createsuperuser with --username attribute. It says there's not parameter like that.
Without username it gives a value error of null Username. Please help.
This is my models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
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')
account = self.model(
email=self.normalize_email(email), username=kwargs.get('username')
)
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)
tagline = models.CharField(max_length=150, 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()
USERNAME_FIELD = 'email'
REQUIRED_FILEDS = ['username']
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