Custom user model in Django and super user - django

I am using Django as backend for my mobile app. When creating users info I need the following info:name, DoB and firebaseUID. Please note that I do not want their email or password. But I want to create superusers who act as admins and want the superusers to be created using email and password. So I am not sure if I have to:
Create a custom user by extending AbstractBaseUser and have a user manager attached to that custom user OR
Create a custom user by extending AbstractBaseUser and then create another Django user class which is only for superusers.
I implemented option 1 with the below code, but I get the following error: "TypeError: MyUserManager.create_superuser() missing 1 required positional argument: 'email'" Also when I am creating superuser, Django is asking for name, firebaseUID and DOB fields although I want to create superusers only email and password fields.
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django.utils import timezone
class MyUserManager(models.Manager):
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.save(using=self._db)
return user
def create_user(self, email=None, password=None, **extra_fields):
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, **extra_fields):
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(email, password, **extra_fields)
def get_by_natural_key(self, email):
return self.get(email=email)
class CustomUser(AbstractBaseUser):
name = models.CharField(null=True, blank=True, max_length=100)
userName = models.CharField(
null=True, blank=True, max_length=100, unique=True)
firebaseUid = models.CharField(
null=True, blank=True, max_length=100, unique=True)
dob = models.DateField(max_length=8)
created_at = models.DateTimeField(default=timezone.now)
email = models.EmailField(unique=True) #used only for createing super user at this point
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
# countryCode = models.CharField(null=True, blank=True, max_length=5)
USERNAME_FIELD = 'userName'
REQUIRED_FIELDS = ['name', 'firebaseUid', 'dob']
objects = MyUserManager()
def __str__(self):
return self.userName
def get_fBaseUid_name(self):
return self.firebaseUid
def has_perm(self, perm, obj=None):
return self.is_superuser
def has_module_perms(self, app_label):
return self.is_superuser
Please let me know if you need any other info. Thank you in advance!
Edit: I already added the following in my settings.py file
AUTH_USER_MODEL = "accounts.CustomUser"
AUTHENTICATION_BACKENDS = ['accounts.backends.FirebaseBackend']
Tl;dr: I want regular users to be created using different fields than creating a superuser.

Has your User model been specified in your settings.py?
If it hasn't, specify it
AUTH_USER_MODEL = "yourapp.UserModel"
Also, in your CustomUser model
Do this
# For creating a superuser using Django
REQUIRED_FIELDS = []
And if you want to create users with "email" and "password", not "username" and "password"
Do this
USERNAME_FIELD = "email"
You can use this code for your create_superuser as well
def create_superuser(self, email, password=None):
if password is None:
raise TypeError("Password should not be none")
user = self.create_user(email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
I hope that helps

Related

How to change DRF endpoints's required fields

I am using jwt.io on my DRF project. As you know jwt.io has already a Login API view called 'TokenObtainPairView' and it requires 2 fields: username and password. But in our project, we want users to log in with their email instead of their username. I handle this with the following code:
class LoginAPIView(TokenObtainPairView):
def post(self, request, *args, **kwargs):
email=request.data['email']
request.POST._mutable = True
profile=ProfileModel.objects.get(email=email)
request.data['username']=profile.username
request.POST._mutable = False
return super().post(request, *args, **kwargs)
It works but on my swagger when front-end devs check the endpoint they see that the endpoint requires 2 fields: username and password. But I want them to see required fields such as email and password.
here is the how my endpoint look like
Is there any way to change its required fields?
I think if you don't need the username you can define your own user model like this.
The USERNAME_FIELD = 'email' on user model says to Django that use the email as username.
Don't forget change settings.py to use your own user model adding this const: AUTH_USER_MODEL = 'users.User' where users is the app name and User is the model name.
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin
)
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""Creates and saves a new user"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
if not email:
raise ValueError('Users must have an email address')
if not password:
raise ValueError('Users must have a password')
"""Creates and saves a new super user"""
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model that suppors using email instead of username"""
email = models.EmailField(max_length=255, unique=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
objects = UserManager()
USERNAME_FIELD = 'email'
By default TokenObtainPairSerializer uses the default username field defined by the model, but in case you need to change the behavior without changing the model you can override the token serializer to use the preferred field, i.e.:
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
username_field = 'email'
class LoginAPIView(TokenObtainPairView):
_serializer_class = CustomTokenObtainPairSerializer

What should i do either extend django base user model or create custom user model?

I am starting a social network kind of website as a hobby project and have knowledge of working with Django but due to less experience I am confused about my user model
my user will have multiple fields and I want to give different permissions.
P.S I am really sorry for being so vague
As Django recommends
If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you.
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
in my experience, it's better to override it too, but i do it by overriding from AbstractBaseUser with a custome manager, so i do what ever i want!
Manager
from django.contrib.auth.base_user import BaseUserManager
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self, username, password, **extra_fields):
if not username:
raise ValueError('The given username must be set')
username = self.model.normalize_username(username)
user = self.model(username=username, **extra_fields)
if password is None:
password = self.make_random_password()
user.set_password(password)
user.save()
return user
def create_user(self, username, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username=username, password=password, **extra_fields)
def create_staff(self, username, password=None, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', False)
return self._create_user(username=username, password=password, **extra_fields)
def create_superuser(self, username, password, **extra_fields):
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(username=username, password=password, **extra_fields)
User model
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import ASCIIUsernameValidator
from django.core.mail import send_mail
from django.db import models
from accounts.managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=150, unique=True,
help_text='Required. 150 characters or fewer. Letters, digits and #/./+/-/_ only.',
validators=[ascii_username_validator],
error_messages={'unique': "A user with that username already exists."})
first_name = models.CharField(max_length=30, null=True, blank=True)
last_name = models.CharField(max_length=150, null=True, blank=True)
is_staff = models.BooleanField('staff status', default=False)
is_active = models.BooleanField('active', default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = UserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = []
def get_full_name(self):
return '{} {}'.format(self.first_name, self.last_name).strip()
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email], **kwargs)
def __str__(self):
return str(self.username)
and then in your settings you should set AUTH_USER_MODEL:
AUTH_USER_MODEL = 'accounts.User'
also, check these links:
django auth
django auth customizing

You are trying to add a non-nullable field 'password' to user without a default; we can't do that

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
# from __future__ import unicode_literals
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.validators import RegexValidator
class UserManager(BaseUserManager):
"""Define a model manager for User model with no username field."""
use_in_migrations = True
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 given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
if password:
user.set_password(password)
user.save(using=self._db)
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, **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)
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 User(AbstractUser):
email = models.EmailField(_('email address'), unique=True, default='email')
username = None
first_name = models.CharField(max_length=20, blank=False, null=False, default='first_name')
last_name = models.CharField(max_length=20, blank=False, null=False, default='last_name')
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+91 ...'")
phone_no = models.CharField(validators=[phone_regex], max_length=17,blank=False, default='phone_number')
# 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)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
# Email & Password are required by default
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
objects = UserManager()
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
# Create your models here.""
I removed the existing users from Database and deleted the previous migration files from migration folder. After again migrations issue has been resolved

Custom User model error: AttributeError: 'CustomUser' object has no attribute 'is_anonymous'

Trying to set up a custom user model that extends the base user. Some user will have login information and some will not, but I still want all users logged. That's what I think the error is saying.
In the default Django model they were logged, but now some users will just have just IP address and username. Other users will have more information like email, etc.
Gettin this error:
AttributeError: 'CustomUser' object has no attribute 'is_anonymous'
Here is my custom user class:
class MyUserManager(BaseUserManager):
def create_user(first_name, last_name, address1, state, zipcode, email, username, password):
user=self.model(
first_name = first_name,
last_name = last_name,
address1 = address1,
zipcode = zipcode,
state = state,
email=email,
username = username,
password = password
)
user.is_superuser = False
user.is_admin = False
user.is_staff = False
user.set_password(password)
user.save(using=self._db)
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)
class CustomUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=25
)
last_name = models.CharField(max_length=25
)
address1 = models.CharField(null=True, max_length=100, blank=True)
zipcode = models.CharField(max_length=10, null=True,blank=True)
state = models.CharField(null=True, max_length=2, blank=True)
email = models.EmailField(max_length = 250)
username = models.CharField(max_length = 25)
password = models.CharField(max_length =25,
null=True)
objects=MyUserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['first_name', 'last_name', 'username', 'email', 'password', 'zipcode']
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
def __str__(self):
return f'{self.first_name} ({self.last_name}) ({self.email})'
How might I get this to work? Any help would be great!
Your CustomUser class must inherit from AbstractBaseUser too. You didn't include it in your code. models.Model is not necessary then.
First you must do :
from django.contrib.auth.models import AbstractBaseUser
And then
class CustomUser(AbstractBaseUser):
###
You can simply add this property in your CustomUser model:
#property
def is_anonymous(self):
"""
Always return False. This is a way of comparing User objects to
anonymous users.
"""
return False

What's the best way to add more fields to the Django User model

I'm a little new to Django. I have created a custom User model because I wanted to use the email for authentication and followed the example in the docs which works well and have the following code:
class CustomUserManager(BaseUserManager):
def _create_user(self, email, password,
is_staff, is_superuser, **extra_fields):
"""
Creates and saves a User with the given email and password.
"""
now = timezone.now()
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email,
is_staff=is_staff, is_active=True,
is_superuser=is_superuser, last_login=now,
date_joined=now, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
return self._create_user(email, password, False, False,
**extra_fields)
def create_superuser(self, email, password, **extra_fields):
return self._create_user(email, password, True, True,
**extra_fields)
class User (AbstractBaseUser, PermissionsMixin):
'''
Custom User Model
'''
email = models.EmailField(verbose_name = "email address", max_length = 255, unique = True)
date_joined = models.DateTimeField('date joined', default=timezone.now)
first_name = models.CharField(_('first name'), max_length=30, blank=False)
last_name = models.CharField(_('last name'), max_length=30, blank=False)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
USERNAME_FIELD = "email"
REQUIRED_FIELDS = []
objects = CustomUserManager()
def get_absolute_url(self):
return "/users/%s/" % urlquote(self.email)
def get_full_name(self):
"""
Returns the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
"Returns the short name for the user."
return self.first_name
def email_user(self, subject, message, from_email=None):
"""
Sends an email to this User.
"""
send_mail(subject, message, from_email, [self.email])
My only issue now is I want to add additional information to my custom User model such as availability date, profile picture, fee etc (I'm making a mini linkedin site). I feel that adding a ton of extra fields to the User model is a bit messy and with time the amount of fields will grow. Is there a better way to do this?
Thanks
There is no reason not to add more fields to this model as you're describing. That's exactly what this custom user model and model migrations are for.
If you're looking to create different user profiles for different users, you could use multi table inheritance, like so:
class User (AbstractBaseUser, PermissionsMixin):
'''
Custom User Model
'''
...fields that are common to CreativeUser and ClientUser
class CreativeUser(User):
# Fields unique to CreativeUser
class ClientUser(User):
# Fields unique to ClientUser
Now, you can query all users using User.objects.all(), you can query CreativeUser.objects.all(), or ClientUser.objects.all(). Note that User.objects.all() will not include any fields that are unique to either of the other two tables.
As for your request i would suggest you to use extension:
django-custom-user