How to change DRF endpoints's required fields - django

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

Related

Custom user model in Django and super user

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

Please enter the correct Email and password for a staff account. Note that both fields may be case-sensitive

I complete makemigrations and migrate then create a superuser. After this
http://127.0.0.1:8000/admin i try to log in then show this error
Please enter the correct Email and password for a staff account. Note that both fields may be case-sensitive
seetings.py
AUTH_USER_MODEL = 'user.CustomUser'
**models.py**
from django.db import models
from django.contrib.auth.models import AbstractUser,BaseUserManager
class CustomUserManager(BaseUserManager):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
def create_user(self, email, password=None):
if not email:
raise ValueError('User must have an email id')
user=self.model(
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None):
user=self.create_user(
email,
password=password
)
user.is_admin = True
user.save(using=self._db)
return user
class CustomUser(AbstractUser):
#only add email field for login
email = models.EmailField(
verbose_name='Email',
max_length=50,
unique=True
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
***admin.py***
from django.contrib import admin
from django.contrib.auth import get_user_model
user=get_user_model()
admin.site.register(user)
i had this problem.
you should define is_staff=True in create_superuser function and you should pass **kwargs as argument in this function.
i hope your problem solve.

How to allow blank password via django-rest-api user create

In my case, I need to have one user group which email and password will not be set on user creation. They will not require to auth themselves either.
I managed to do custom user with a nullable email, but I cannot find a way to allow blank password in API call or Django admin forms. I do not really care much about forms, but I need to have it working via API.
How could I allow to create a new custom user with a blank password and maybe set it to something meaningful if it comes blank (like set_unusable_password())?
Thanks!
My CustomUserManager:
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, **extra_fields):
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
# if password == "":
# user.set_password(None)
# else:
user.set_password(password)
user.save()
return user
def create_superuser(...
You can base your CustomUser on AbstractUser and use set_password
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
...
Thanks to that, you have access to the function set_password. Any user based on CustomUser will inherit :
def set_password(self, raw_password):
self.password = make_password(raw_password)
self._password = raw_password
And if you look closely make_password you will realize it possible to give it None :
if password is None:
return UNUSABLE_PASSWORD_PREFIX + get_random_string(UNUSABLE_PASSWORD_SUFFIX_LENGTH)
You could check the full example on Django to have a starting point with a Manager
Your model will look like :
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
UPDATE after you upload your Manager :
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(...
You forgot the password=None in your create_user. That is why you have this error required.
This helped me:
how to mention password field in serializer?
I have declared password field in user serializer and set required=False
class CustomUserSerializer(serializers.ModelSerializer):
password = serializers.CharField(
write_only=True,
required=False,
)
class Meta:
model = CustomUser
fields = ("id", "email", "password")
def create(self, validated_data):
validated_data['password'] = make_password(validated_data.get('password'))
user = CustomUser.objects.create_user(**validated_data)
Token.objects.create(user=user)
return user

customising django user model is failing

**Hi
I am trying to customize the django default user. Applying migrations works fine. However I get an error when i try to create a super user. Can you please advise why I get this error?
******************** self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
File "/home/gravityns/PycharmProjects/dev/shop/models.py", line 54, in create_superuser
user.is_staff = True
AttributeError: can't set attribute
# accounts.models.py
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
from django.core.validators import RegexValidator
class UserManager(BaseUserManager):
def create_user(self, username, password, email):
"""
Creates and saves a User with the given email and password.
"""
if not username:
raise ValueError('Users must have a username')
if not email:
raise ValueError('Users must have an email address')
user = self.model(
username = username,
email = self.normalize_email(email),
)
user.set_password(password)
user.save(using=self._db)
return user
def create_staffuser(self, username, password, email):
"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
username,
email,
password,
)
user.is_staff = True
user.save(using=self._db)
return user
def create_superuser(self, username, password, email):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.create_user(
username,
email,
password
)
user.is_staff = True
user.is_admin = True
user.save(using=self._db)
return user
USERNAME_REGEX = '^[a-zA-Z0-9.#+-]*$'
class User(AbstractBaseUser):
username = models.CharField(max_length=255, validators=[
RegexValidator(regex= USERNAME_REGEX,
message = 'Username must be Alphanumeric or any of the following: ". # + -"')],
unique=True
)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
created_at = models.DateField(auto_now_add=True, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False) # a admin user; non super-user
# notice the absence of a "Password field", that's built in.
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email'] # 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
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, 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.is_staff
#property
def is_admin(self):
"Is the user a admin member?"
return self.is_admin
#property
def is_active(self):
"Is the user active?"
return self.is_active
You're overwriting the normal is_staff attribute with your custom property. You need to rename those properties so that they don't clash with the stuff AbstractBaseUser already implements. Look at how that class implements is_staff and others etc here: https://github.com/django/django/blob/master/django/contrib/auth/models.py#L288
You have a field on User model as User.is_staff and a #property with the same name. So you can't set the property of the model object by user_instance.is_staff = True.
You can have is_staff as a field or as a class property, not both.
You have unncessarily defined properties for is_staff, is_active and is_superuser. But those are already fields, with the exact same name. Therefore, when you try and set user.is_staff = True, Python access your property, and tries to "set" that, rather than the field.
There is no reason to do this. The only reason you would need to define those properties is if you want to disallow setting the fields themselves. But you don't want to do that. (Alternatively, if you didn't want fields at all, but wanted to base the value on some custom logic - but, again, you wouldn't be able to set them unless you defined a custom setter.)
Remove all three of those properties.

Django custom user model gives error: TypeError: create_superuser() got an unexpected keyword argument 'username'

I'm creating a Django custom user based on a tutorial as below:
class UserProfileManager(BaseUserManager):
"""Helps Django work with our custom user model."""
def create_user(self, email, name, password=None):
"""Creates a user profile object."""
if not email:
raise ValueError('Users must have an email address.')
email = self.normalize_email(email)
user = self.model(email=email, username=name)
user.user_id = -1
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, name, password):
"""Creates and saves a new superuser with given details."""
user = self.create_user(email = email, name = name, password = password)
user.is_superuser = True
user.save(using=self._db)
class CustomUser(AbstractBaseUser, PermissionsMixin):
"""Represents a user profile inside our system"""
email = models.EmailField(max_length=255, unique=True)
username = models.CharField(max_length=255, unique=True)
user_id = models.IntegerField()
objects = UserProfileManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def __str__(self):
return self.email
And I get the following error when I try to create a superuser:
TypeError: create_superuser() got an unexpected keyword argument 'username'
However if I change the name of "username" field to "name", I will be able to create a superuser with no error!
Does anyone know why can't I name the field anything other than "name"?
On your function you defined the username parameter as name:
...
def create_superuser(self, email, name, password):
...
And in some other part of your code or even inside django itself. Maybe someone is calling your create_superuser function with username='some-username'.