using custom model fo django djoser - python-3.x

i am creating api endpoints for user management using Djoser and i want to use a custom model to create user and login i only want to use email.
the user entity given to me does not have a username field
below i will share the various settings i have set up for my apps
#accounts/model.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
username = None
email = models.EmailField(unique=True)
REQUIRED_FIELDS = ['first_name', 'last_name']
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
My serializer file
#accounts/serializers.py
from djoser.serializers import UserCreateSerializer, UserSerializer
from rest_framework import serializers
from rest_framework.fields import CurrentUserDefault
from .models import CustomUser
class UserCreateSerializer(UserCreateSerializer):
class Meta:
model = CustomUser
fields = ['id', 'email', 'first_name', 'last_name']
#settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSIONS_CLASSES': (
'rest_framework.permissions.IsAuthenticated'
)
}
AUTH_USER_MODEL = 'accounts.CustomUser'
DJOSER = {
'LOGIN_FIELD': 'email',
'USER_CREATE_PASSWORD_RETYPE': True,
'SERIALIZERS': {
'user_create': 'accounts.serializers.UserCreateSerializer',
'user': 'accounts.serializers.UserCreateSerializer',
# 'current_user': 'accounts.serializers.CurrentUserSerializer'
}
when i try to register user i get
TypeError at /auth/users/
create_user() missing 1 required positional argument: 'username'
Request Method: POST
Request URL: http://127.0.0.1:8000/auth/users/
Django Version: 3.1
Exception Type: TypeError
Exception Value:
create_user() missing 1 required positional argument: 'username'
Exception Location: /home/femiir/.virtualenvs/codegarage/lib/python3.8/site-packages/djoser/serializers.py, line 73, in perform_create
Python Executable: /home/femiir/.virtualenvs/codegarage/bin/python
Python Version: 3.8.5
please what i my doing wrong ?

You need to have a custom user manager, probably something like this:
from django.contrib.auth.base_user import BaseUserManager
class MyUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves a User with the given email, first name,
last name and password.
"""
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=None, **extra_fields):
"""
Creates and saves a superuser with the given email, first name,
last name and password.
"""
user = self.create_user(
email,
password=password,
**extra_fields,
)
user.is_admin = True
user.save(using=self._db)
return user
And in your custom user model:
class CustomUser(AbstractBaseUser):
# [...]
objects = MyUserManager()
# [...]
I've taken the code from the django documentation about customizing the User model. They provide an example using the email as the username field (which is what you want).
You may keep the inheritance from AbstractUser but if you do not need most of the things that are in that model, you can also inherit your model from AbstractBaseUser, as in the example.

Related

ValueError: Related model 'users.customuser' cannot be resolved

Can't figure out what to do to solve this problem. I am trying to create a custom user model but when i try to migrate it throws this error.
Here is managers.py:
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifiers
for authentication 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 or Phone numvber 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)
Here is models.py:
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.CharField(_('email or phone number'), max_length=175, 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 = CustomUserManager()
def __str__(self):
return self.email
Thanks in advance, please explain what went wrong
Try this steps
settings.py
AUTH_USER_MODEL = 'your_app_name.CustomUser'
Steps
--- Delete `migrations` folder
--- also delete `db.sqlite3` (database file)
--- run command `python manage.py makemigrations your_app_name`
--- then run `python manage.py migrate`

How to create a custom user model with an email and/or a phone number as a login with Django REST framework?

I want to create the custom user model with the email and/or the phone number as a login. I understood how make them separately but how to combine them together?
For instance, should i do the same like this but instead of the email the phone number?
Managers.py :
rom django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where email is the unique identifiers
for authentication 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)
Models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _
from .managers import CustomUserManager
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = CustomUserManager()
spouse_name = models.CharField(blank=True, max_length=100)
date_of_birth = models.DateField(blank=True, null=True)
def __str__(self):
return self.email
settings.py:
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
ACCOUNT_CONFIRM_EMAIL_ON_GET = True
ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = '/?verification=1'
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = '/?verification=1'
Thanks in advance, when you write an answer if you don't mind add a link to a useful material

Django login/register issue

I am new to Django and am building a database-driven website using PyCharm.
I am having an issue with users registering/logging in. What is happening is, when a user registers, I check the "Database" tab to the right, and the information will be passed into a table named "SavBlock_user", which will have the users name, password, etc.. Then, when I try to log in, it won't allow me to login due to incorrect username/password. However, if I try to login using a username/password from a different table named "auth_user" (like username: admin / password: admin), then I can successfully login. I'm not sure how to fix this.
Ideally, what I would like to do is completely remove the "SavBlock_user" table and strictly use "auth_user" for all of my users, but I'm not sure how to do this. I may have created a 'custom' user model back when I was learning the system, but I can't remember.
My files:
Project\register\forms.py
from django import forms
from SavBlock.models import * # <--- Contains User
''' Form for users to register '''
class RegisterForm(forms.ModelForm):
email = forms.EmailField(
initial='myemail#savagez.com'
)
uso_validate = forms.BooleanField(
label='Are you a PSMC member? (Chief, Uso, Anak)',
initial=False
)
class Meta:
model = User
widgets = {
'password': forms.PasswordInput(),
}
fields = '__all__'
Project\register\views.py
from django.http import request
from django.shortcuts import render, redirect
from .forms import RegisterForm
# Create your views here.
def register(response):
if response.method == "POST":
form = RegisterForm(response.POST or None)
if form.is_valid():
form.save()
return redirect('/dashboard/')
else:
form = RegisterForm()
return render(response, 'register/register.html', {'form': form})
Project\SavBlock\models.py
from django.db import models
class User(models.Model):
username = models.CharField("user name", max_length=50, default='')
email = models.EmailField("email address", unique=True, default='DEFAULT VALUE')
first_name = models.CharField("first name", max_length=50)
last_name = models.CharField("last name", max_length=50)
password = models.CharField("password", unique=True, max_length=50, default='')
rank = {
0: 'Supporter',
1: 'Anak',
2: 'Uso',
3: 'Chief'
}
#TODO: FIT __INIT__
'''
def __init__(self, first_name, last_name, *args, **kwargs):
super().__init__(*args, **kwargs)
self.first_name = first_name.title()
self.last_name = last_name.title()
'''
# Magic method returns string of self
def __str__(self):
return f"User {self.first_name} {self.last_name} rank {self.rank}".strip()
#property
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
class Anak(User):
def __init__(self, first_name, last_name, tribe):
super().__init__(first_name, last_name)
self.tribe = tribe.title()
self.rank = User.rank[1]
class Uso(User):
def __init__(self, first_name, last_name, tribe):
super().__init__(first_name, last_name)
self.tribe = tribe.title()
self.rank = User.rank[2]
----EDIT----
I fixed the different user tables and basically reset the migrations. Now, all users are showing up under a single user table SavBlock_user. However, the login issue is still there.
Admin was created using the terminal manage.py createsuperuser command. I am able to login on the webpage using this account.
testing123 was created using the registration form. It pulls up a message that says "Please enter a correct username and password".
Anyone have any ideas?

Why doesn't DRF enforce non-empty field?

I am using a custom user model:
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
username = models.CharField(max_length=120,
unique=True,
blank=False,
validators=[MinLengthValidator(6)])
email = models.EmailField('Email Address', unique=True, blank=False)
FCM_ID = models.CharField(max_length=300)
I was hoping that I won't be allowed to create a user with blank email field, but for some reason, I can do so without any errors.
User().save() successfully adds a user with all blank fields. The unique condition, however, is enforced as expected. Why is blank=False not enforced?
Here's the custom user manager:
from django.contrib.auth.base_user import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, **kwargs):
if not email:
raise ValueError("The email must be set")
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.save()
return user
def create_superuser(self, email, password, **kwargs):
...
Edit: I made the email field blank=False as well; but to no avail.
Unfortunately, blank=False is not enforced on the database level, it is only checked when you're validating model in your code, which is not happening by itself when calling save() on it. You need to invoke clean_fields at least before saving the model.
Read about validating models in Django docs.
If you are using Django models through DRF serializers or Django forms, they may do some validation as well, so refer to their documentation to find out how it will be handled.

user.check_password assertion failing

I have some tests trying to validate my user creation process in an API application.
The issue I am having is that when I try to validate a users password was created as a hashed object the test is failing.
Test:
CREATE_USER_URL = reverse('user:create')
def create_user(**params):
return get_user_model().objects.create_user(**params)
...
def test_create_valid_user_success(self):
"""Test creating user with valid user is successful"""
payload = {
'email': 'test#email.com',
'password': 'testpass',
'name': 'Test Name'
}
res = self.client.post(CREATE_USER_URL, payload)
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
user = get_user_model().objects.get(**res.data)
self.assertTrue(user.check_password(payload['password']))
self.assertNotIn('password', res.data)
Serializer:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
"""Serializer for users object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name')
extra_kwargs = {
'password': {
'write_only': True,
'min_length': 8
}
}
def create(self, validated_data):
"""Create a new user with encrypted password and return it"""
return get_user_model().objects.create_user(**validated_data)
Failed Test:
FAIL: test_create_valid_user_success (user.tests.test_user_api.PublicUserApiTests)
Test creating user with valid user is successful
----------------------------------------------------------------------
Traceback (most recent call last):
File "/app/user/tests/test_user_api.py", line 33, in test_create_valid_user_success
self.assertTrue(user.check_password(payload['password']))
AssertionError: False is not true
From what I can tell from documentation etc, I have the correct syntax.
Wjhat is missing/incorrect that I need in order to fix this error?
Turns out the issue was indentation in my UserSerializer class.
See this issue:
Django users being created with cleartext passwords

Resources