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.
Related
After creating a custom user model in Django and trying to edit a user in Django admin i get an IntegrityError.
I get a IntegrityError at /admin/accounts/customuser/add/ FOREIGN KEY constraint failed when I delete a user in Django admin. This also happens when I try to add a user in Django admin.
Code
models.py
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models
from django.utils.translation import gettext_lazy as _
class UserManager(BaseUserManager):
def _create_user(self, email, password, **kwargs):
if not email:
raise ValueError("Email is required")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_user(self, email, password=None, **extra_fields):
"""Create and save a regular User with the given email and password."""
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **kwargs):
kwargs.setdefault('is_staff', True)
kwargs.setdefault('is_superuser', True)
kwargs.setdefault('is_active', True)
if kwargs.get('is_staff') is not True:
raise ValueError("Superuser must have is_staff True")
if kwargs.get('is_superuser') is not True:
raise ValueError("Superuser must have is_superuser True")
return self._create_user(email, password, **kwargs)
class CustomUser(AbstractUser):
username = None
first_name = None
last_name = None
email = models.EmailField(_('email address'), blank=False, null=False, unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
full_name = models.CharField(_('full name'), max_length=1000, null=True, blank=True)
user_id = models.CharField(_('session id'), max_length=10000, null=True, blank=True)
verification_code_time = models.IntegerField(_('time left for session id'), null=True, blank=True)
verification_code = models.IntegerField(_('verification code'), null=True, blank=True)
objects = UserManager()
def __str__(self):
return self.email
admin.py
from django.contrib import admin
from .models import CustomUser
admin.site.register(CustomUser)
Any suggestions?
I'm trying to create categories for my listings but it shows the above error.
I also tried adding the id column manually but then it shows this error:
AssertionError: Model auctions.Category can't have more than one auto-generated field.
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser,BaseUserManager
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
# Create your models here.
class myUserManager(BaseUserManager):
"""
custom user model manager where email is unique indentifiers for authenticaton
instead of usernames.
"""
def create_user(self, email, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
if not email:
raise ValueError(_('The Email must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff',True)
extra_fields.setdefault('is_superuser',True)
extra_fields.setdefault('is_active',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(email,password, **extra_fields)
class myUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = myUserManager()
def __str__(self):
return f'{self.email}'
class Category(models.Model):
category = models.CharField(max_length=50, default="No Category")
def __str__(self):
return f'{self.category}'
class Listings(models.Model):
listing_name = models.CharField(max_length=50)
price = models.IntegerField(default=1)
date_listed = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
category = models.ForeignKey(Category,on_delete=models.DO_NOTHING,null=True,blank=True)
item_image = models.ImageField()
description = models.TextField(max_length=200, default="Description Not Available")
listed_by = models.ForeignKey(myUser,on_delete=models.CASCADE, default=1,null=True)
def __str__(self):
return f'{self.listing_name}'
class Bid(models.Model):
item_name = models.ForeignKey(Listings,on_delete=models.CASCADE)
bid_by_user = models.ForeignKey(myUser,on_delete=models.CASCADE)
new_bid = models.IntegerField()
Please tell me what to do and if there is any better way to do it please do tell me I'd really appreciate it.
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'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)
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