I modified the Auth User by using AbstractUser Class.
Registered it in settings. Everything else is working
I can create an instance using the User Model
BUT
The Problem comes during the serialization.
My serializer doesn't throw any error, but any time I try and use the serializer.data I get AttributeError: 'str' object has no attribute '_meta'.
User Model
class User(AbstractUser):
# Add additional fields here
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=100)
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
first_name=None
last_name=None
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'username', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
Custom Manager
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import gettext_lazy as _
class CustomUserManager(BaseUserManager):
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)
User Serializer
from rest_framework import serializers
from django.conf import settings
User = settings.AUTH_USER_MODEL
class UserSerializer(serializers.ModelSerializer):
print("UserSerializer")
class Meta:
model = User
# fields = (['id', 'username', 'email', 'name'])
fields = '__all__'
View
#api_view(['POST'])
def createUser(request):
data = request.data
serializer = UserSerializer(data,many=False)
if serializer.is_valid():
print(type(serializer.data))
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I've stuck with the code for the last 7-8 hours. Even tried specifying the fields but I haven't been able to figure out why this is happening.
You should work with the get_user_model() function [Django-doc] to obtain a reference to the user model, the AUTH_USER_MODEL setting [Django-doc] is only a string to the name of that model:
from rest_framework import serializers
from django.contrib.auth import get_user_model
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = '__all__'
Related
I am trying to create a new user from the modelform by calling the CustomerUserManager in managers.py
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(CustomUserCreationForm,self).__init__(*args, **kwargs)
try:
if args[0]["email"]:
self.email= args[0]["email"]
self.password= args[0]["password1"]
except:
pass
class Meta:
model = CustomUser
fields = "__all__"
def save(self, **kwargs):
return CustomUserManager.create_user(self,email=self.email,password=self.password,name="hic")
The managers.py is
class CustomUserManager(BaseUserManager):
def create_user(self, email, password, name,**extra_fields):
...
user = self.model(email=email,name=name, stripe_customer_id=customer.id ,**extra_fields)
user.set_password(password)
user.save()
return user
Obviously the self.model does not exist, so how can I correctly create that user from the modelform? If I import the model, I get circular error import.
here is my model.py
class CustomUser(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
name = models.CharField(verbose_name=_("first name"), max_length=50)
stripe_customer_id = models.CharField(max_length=120)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
objects = CustomUserManager()
I'm not sure why you would want a save function in your form, rather than in your view using the model's save() function after your form data has validated...
That said, if you do want a create_user function in your manager, you would call it like
from django.contrib.auth import get_user_model
get_user_model().objects.create_user(...)
This should ensure the model is present to be referred to.
I am unable to authenticate custom user in django==3.1.3
Here is Custom User Manager:
from django.contrib.auth.base_user import BaseUserManager
from django.utils.translation import ugettext_lazy as _
class CustomUserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError(_('Users should have a username.'))
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(_('Password should not be empty.'))
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
Here's the CustomUser model:
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from .managers import CustomUserManager
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255, unique=True, db_index=True)
is_verified = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = CustomUserManager()
def __str__(self):
return self.email
I have added AUTH_USER_MODEL = 'authentication.CustomUser' in settings.py
(authentication is the app name)
In the shell, if I run these, I get authenticated_user as None:
user = CustomUser.objects.create(email='tejas#gmail.com', username='tejas12', password='tejas12')
authenticated_user = authenticate(email='tejas#gmail.com', username='tejas12', password='tejas12')
However, the User gets created successfully with given details.
Also, check_password returns False:
from django.contrib.auth.hashers import check_password
user.check_password('tejas12') # returns False
On creating a user using python manage.py createsuperuser,
authenticate returns the required user, and
check_password returns True
How should I authenticate the custom users?
In your example, you create the user as a regular Django model which doesn't hash password properly
user = CustomUser.objects.create(email='tejas#gmail.com', username='tejas12', password='tejas12')
create_user method should be used instead
>>> second_user = CustomUser.objects.create_user(email='test#example.com', username='test', password='test_passwd')
>>> second_user.check_password('test_passwd')
True
Authentication uses default backend unless you specify your own and will rely on the USERNAME_FIELD and password
>>> from django.contrib.auth import authenticate
>>> authenticate(email='test#example.com', username='test', password='test_passwd')
>>> authenticate(email='test#example.com', password='test_passwd')
<CustomUser: test#example.com>
When trying to authenticate a user created through a view in DRF Browsable API, I get
No active account found with the given credentials
The view:
class MyUserCreate(APIView):
def post(self, request, format='json'):
serializer = MyUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The serializer:
class MyUserSerializer(serializers.ModelSerializer):
username = serializers.CharField(
required=True,
validators=[UniqueValidator(queryset=MyUser.objects.all())],
min_length=5,
max_length=20
),
password = serializers.CharField(
required=True,
max_length=256
)
class Meta:
model = MyUser
fields = ('username', 'password')
def create(self, validated_data):
password = make_password(validated_data['password'])
user = MyUser.objects.create_user(validated_data['username'], password)
return user
The password is being hashed. At this stage, went on to the admin page and tested to login there too with the created account and got
Made sure the custom user model had is_active, is_superuser and is_staff and checked if that would fix the issue but it didn't.
class MyUserManager(BaseUserManager):
def create_user(self, username, password, **extra_fields):
user = self.model(
username=username
)
user.is_staff=True
user.is_active=True
user.is_superuser=True
user.set_password(password)
user.save(using=self._db)
return user
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, password, **extra_fields)
class MyUser(AbstractBaseUser):
objects = MyUserManager()
class Meta:
# managed = False
db_table = 'user_entity'
user_id = models.AutoField(primary_key=True, db_column='userId')
username = models.CharField(db_column='username', unique=True, max_length=20)
password = models.CharField(db_column='userPassword', max_length=256)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=True)
USERNAME_FIELD = 'username'
def __str__(self):
return str(self.user_id) + " (%s)" % str(self.username)
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
Then, tried to create the user using python manage.py createsuperuser... doing so then I'm able to both login in the admin pannel and authenticate that user with the Token Obtain Pair view shown initially.
First of all. Thank you for providing all the details. It is always easier to debug. Coming to the issue, the problem is that you are using make_password explicitly.
If you would look through the Django's set_password documentation, you'd find that it takes care of hashing.
What you are doing is, first you are hashing your password, and providing it as a raw value to set_password method via create_user. The set_password would assume that as a raw value and would hash it again. Hence, your original password is not used at all.
You can just remove the use of make_password and instead change your serializer's create method to
class MyUserSerializer(serializers.ModelSerializer):
...
def create(self, validated_data):
user = MyUser.objects.create_user(validated_data['username'], validated_data["password"])
return user
This should resolve your query. I hope it helps! :)
I followed a tutorial on how to allow Django authentication by using email instead of the normal username, however, they didn't mention how to register the new custom user model with the admin page! I am a real beginner to Django so I would appreciate as much detail as possible! Here is my code so far:
managers.py
from django.contrib.auth.models import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Email field is required')
email = self.normalize_email(email)
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
return self.create_user(email, password, **extra_fields)
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 CustomUserManager
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True, null=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. ''Unselect this instead of deleting accounts.'
),
)
USERNAME_FIELD = 'email'
objects = CustomUserManager()
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self):
return self.email
def get_short_name(self):
return self.get_full_name()
def __str__(self):
return self.email
admin.py
from django.contrib import admin
# Register your models here.
try this code in your admin.py
We can register our models with register decorator.
from django.contrib import admin
from .models import CustomUser
#admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
pass
from .models import CustomUser
class CustomUserAdmin(admin.ModelAdmin):
list_display = ('email',...) # add fields as you want
admin.site.register(CustomUser, CustomUserAdmin)
I have a custom user model and a user manager defined as follows:
/accounts/models.py
from django.contrib.auth.models import (
AbstractBaseUser,
BaseUserManager,
PermissionsMixin
)
from django.db import models
from django.utils import timezone
class UserManager(BaseUserManager):
def create_user(self, email, first_name, last_name, username=None, password=None):
if not email:
raise ValueError("Users must have a valid email address")
if not first_name and last_name:
raise ValueError("Users must have a first and last name")
created_username = ''.join([first_name.lower(), last_name[:1].lower()])
i=2
while User.objects.filter(username=created_username).exists():
created_username = ''.join([first_name.lower(), last_name[:i].lower()])
i+=1
user = self.model(
email=self.normalize_email(email),
first_name=first_name,
last_name=last_name,
username=created_username
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, first_name, last_name, password):
user = self.create_user(
email,
first_name,
last_name,
password
)
user.is_staff = True
user.is_admin = True
user.is_superuser = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
username = models.CharField(max_length=40, unique=True, blank=True, editable=False)
# display_name = models.CharField(max_length=150)
bio = models.TextField(blank=True, null=True)
avatar = models.ImageField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name','last_name']
def __str__(self):
return "{} #{}".format(self.email, self.username)
def get_short_name(self):
return self.first_name
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
This seems to work perfectly when registering a superuser from the shell. I have a form and a view set up to register regular users on my site as follows:
/accounts/forms.py
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
auth_code = 'hamburger'
def validate_authorization(value):
if value != auth_code:
raise ValidationError(
_('Must have valid authorization code in order to register.')
)
class UserCreateForm(UserCreationForm):
authorization_code = forms.CharField(max_length=10, required=True, validators=[validate_authorization])
class Meta:
model = get_user_model()
fields = ("email", "first_name", "last_name", "password1", "password2", "authorization_code")
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["email"].label = "Email Address"
self.fields["first_name"].label = "First Name"
self.fields["last_name"].label = "Last Name"
self.fields["password1"].label = "Password"
self.fields["password2"].label = "Password Confirmation"
self.fields["authorization_code"].label = "Authorization Code"
/accounts/views.py
from django.shortcuts import render
from django.template import RequestContext
from django.contrib.auth import login, logout
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.core.urlresolvers import reverse_lazy
from django.views import generic
from django.http import HttpResponseRedirect
from django.contrib.auth import get_user_model
from . import forms
class SigninView(generic.FormView):
form_class = AuthenticationForm
success_url = '/dashboard/' #reverse_lazy('index')
template_name = 'accounts/signin.html'
def get_form(self, form_class=None):
if form_class is None:
form_class = self.get_form_class()
return form_class(self.request, **self.get_form_kwargs())
def form_valid(self, form):
login(self.request, form.get_user())
return super().form_valid(form)
class SignoutView(generic.RedirectView):
url = '/' #reverse_lazy("home")
def get(self, request, *args, **kwargs):
logout(request)
return super().get(request, *args, **kwargs)
class RegisterView(generic.CreateView):
form_class = forms.UserCreateForm
success_url = '/'
template_name = 'accounts/register.html'
def form_valid(self, form):
self.object = form.save(commit=False)
form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:1].lower()])
i=2
while get_user_model().objects.filter(username=form.instance.username).exists():
form.instance.username = ''.join([form.instance.first_name.lower(), form.instance.last_name[:i].lower()])
i+=1
form.save()
return HttpResponseRedirect(self.get_success_url())
# return super(RegisterView, self).form_valid(form)
I am at a loss as to why my superuser cannot log into the website but my regular users can. Also you will notice I have a while statement that auto generates a username based on the entered first and last name. Initially I had this only in the UserManager however, the form was bypassing the user manager and so I had to add the same block of code to my view. So there seems to be a disconnect between users created from the form versus users created from the shell (UserManager).
The authorization_code is in place because I don't want just anybody to be able to register on my site and I didn't know a better way. I am open to better suggestions.
Additional information that may be helpful
settings.py
# Set user authentication model
AUTH_USER_MODEL = 'accounts.User'
Python 3.5, Django 1.10
Thank you in advance for any advice or insight.
Problem solved.
In
def create_superuser(self, email, first_name, last_name, password):
user = self.create_user(
email,
first_name,
last_name,
password
)
I was forgetting to set password=password,. From looking at the password field in the database, it seems this was also resulting in (as close as I can tell) bypassing <algorithm>$<iterations>$<salt> (per the Django docs https://docs.djangoproject.com/en/1.10/topics/auth/passwords/) though the password was still being hashed in some way (not being stored in plain text) the password field for superusers was considerably shorter than the password field for normal users. Whatever it was doing, it was not storing the actual password and was giving me an invalid username/password when attempting to log in with a superuser account.
So the proper way is
def create_superuser(self, email, first_name, last_name, password):
user = self.create_user(
email,
first_name,
last_name,
password=password,
)
I still don't understand why created_username is being bypassed in the UserManager when saving a user from the AuthenticationForm but I found a workaround by adding the same while statement to the view. At least all is functional now. I'm still interested to learn if anybody has further insight into this matter.