django AttributeError: dict object has no attribute 'pk' - django

I'm making a simple django rest framework project.
This is just creating a new user, and logging in.
When I used django basic auth user model, everything worked well.
But after changing basic user model to custom user, this error comes out when creating a new user:
dict object has no attribute 'pk'
Custom user model is made referred to django docs.
Error says that:
File "/home/seokchan/server/mdocker/lib/python3.5/site-packages/django/contrib/auth/__init__.py",
line 100, in login
if _get_user_session_key(request) != user.pk or ( AttributeError: 'dict' object has no attribute 'pk'
This seems to say that user model has no pk, but I don't get it.
models.py
class MyUserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have an user name')
user = self.model(
email=self.normalize_email(email),
username = username
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password):
user = self.create_user(
username,
password=password,
email = email,
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
id = models.AutoField(primary_key=True)
username = models.CharField(
verbose_name='user name',
max_length=30,
unique=True,
)
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
sth_test = models.TextField(blank = True)
objects = MyUserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email']
def __str__(self):
return self.username
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?"
# Simplest possible answer: All admins are staff
return self.is_admin
serializers.py
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model
fields = ('id', 'username', 'email', 'password', 'is_active')
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())]
)
username = serializers.CharField(
max_length=32,
validators=[UniqueValidator(queryset=User.objects.all())]
)
password = serializers.CharField(min_length=8, write_only=True)
def validate_email(self,value):
if User.objects.filter(email=value).exists():
raise serializers.ValidationError("err.")
return value
def create(self, validated_data):
user = User.objects.create_user(
validated_data['username'],
validated_data['email'],
validated_data['password'],
)
user.is_active = False
user.save()
message=render_to_string('accounts/account_activate_email.html',{
'user':user,
'domain':'localhost:8000/api/accounts/activate',
'uid':urlsafe_base64_encode(force_bytes(user.pk)).decode('utf-8'),
'token':account_activation_token.make_token(user)
})
mail_subject = 'Bplus'
to_email = user.email
AuthEmail = EmailMessage(mail_subject, message, to=[to_email])
AuthEmail.send()
return validated_data
views.py
class UserCreateAPI(generics.GenericAPIView):
serializer_class = CreateUserSerializer
def post(self, request, *args, **kwargs):
if len(request.data["username"]) < 4 or len(request.data["password"]) < 8:
body = {"message":"short field"}
return Response(body, status = 400)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
user_for_auth = User.objects.get(username=user['username'])
login(request, user)
return Response(
{
"user":UserSerializer(
user, context=self.get_serializer_context()
).data,
"token":AuthToken.objects.create(user_for_auth),
}
)
How can I fix this error?

Your serializer create method returns the validated data instead of the created object. Since that is a dict, that is what you end up passing to the login function.
You should have return user instead of return validated_data.

Related

Not hashing password. Unexpected keyword argument 'password2'

I have three different questions. They're all related to each other.
1 - I get an error when I add password2 or confirm_password field.
Got a `TypeError` when calling `CustomUser.objects.create()`.
This may be because you have a writable field on the serializer class that is not a valid argument to `CustomUser.objects.create()`.
You may need to make the field read-only, or override the UserCreateSerializer.create() method to handle this correctly.
TypeError: CustomUser() got an unexpected keyword argument 'confirm_password'
2 - Without any validate when I only have password field. I am not getting any errors but passwords are not hashed.
3 - When I want to create user using shell. Even if I leave all the fields blank, I can create an empty user without any errors.
Custom User Manager
class CustomUserManager(BaseUserManager):
def create_user(self, email, password=None,**kwargs):
if not email:
raise ValueError('Email is required!')
user = self.model(
email = self.normalize_email(email),
**kwargs,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **kwargs):
user = self.create_user(
email,
password=password,
**kwargs
)
user.is_admin = True
user.save(using=self._db)
return user
Custom User
class CustomUser(AbstractBaseUser):
email = models.EmailField(_('Email'), max_length=50, unique=True)
username = models.CharField(_('Username'),max_length=50)
first_name = models.CharField(_('First Name'),max_length=50)
middle_name = models.CharField(_('Middle Name'),max_length=50, blank=True, null=True)
last_name = models.CharField(_('Last Name'),max_length=50)
desc = models.TextField(_('Description'), blank=True, default='Nothing here.')
created_at = models.DateTimeField(auto_now_add=True)
modified_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
"Is the user a member of staff?"
return self.is_admin
User Serializer
User = get_user_model()
class UserCreateSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True, validators =[validators.UniqueValidator(queryset=User.objects.all())])
password = serializers.CharField(write_only=True, required=True, validators=[validate_password])
confirm_password = serializers.CharField(write_only=True, required=True)
class Meta:
model = User
fields = [
'email',
'username',
'first_name',
'middle_name',
'last_name',
'password',
'confirm_password',
]
def validate(self, data):
email = data['email']
print(data)
user_qs = User.objects.filter(email=email)
if user_qs.exists():
raise serializers.ValidationError('This user has already registered!')
if data['password'] != data['confirm_password']:
raise serializers.ValidationError('Passwords didn\'t match!')
elif data.get('password') == data.get('confirm_password'):
data['password'] = make_password(
data.get('password')
)
data.pop('confirm_password', None)
return data
def create(self, validated_data):
username = validated_data['username']
email = validated_data['email']
first_name = validated_data['first_name']
middle_name = validated_data['middle_name']
last_name = validated_data['last_name']
user = User.objects.create(
username = username,
email = email,
first_name=first_name,
middle_name=middle_name,
last_name=last_name,
)
user.set_password(validated_data['password'])
user.save()
return user

Unable to login because Django saving passwords in plain text but

class UserManager(BaseUserManager):
def create_user(self, username, email, password=None):
if username is None:
raise TypeError('User should have a username')
if email is None:
raise TypeError('User should have an email')
user = self.model(
username=username,
email=self.normalize_email(email)
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, username, email, password=None):
if password is None:
raise TypeError('Password should not be none')
user = self.create_user(username, email, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
class User(AbstractBaseUser, PermissionsMixin):
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_staff = models.BooleanField(default = True)
is_active = models.BooleanField(default = True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def __str__(self):
return self.email
def tokens(self):
refresh = RefreshToken.for_user(self)
return {
'refresh': str(refresh),
'access': str(refresh.access_token)
}
class RegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(
max_length = 255,
min_length = 6,
write_only = True
)
class Meta:
model = User
fields = ['email', 'username', 'password']
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def validate(self, attrs):
email = attrs.get('email', '')
username = attrs.get('username', '')
if not username.isalnum():
raise serializers.ValidationError(
"Username should contain only alphanumeric characters"
)
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class RegisterView(generics.GenericAPIView):
permission_classes = (AllowAny,)
serializer_class = RegisterSerializer
def post(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(
serializer.data,
status=status.HTTP_201_CREATED
)
Currently I'm using django 3.1.3 and djangorestframework 3.12.2. While I'm able to login with superuser and getting tokens properly but couldn't login with staff users. From Django admin I have seen only superuser has hashed password and all other users have plain text password.
You do not set the password with user.password, that is just a text field on a model and works like any other. You must either:
preferred: Call user.set_password(value)
backup: Calculate the password manually using make_password before saving
You already have a create method so we'll work with that first.
from django.contrib.auth.hashers import make_password
def create(self, validated_data):
pwd = validated_data.pop("password")
user = User.objects.create(**attrs)
user.set_password(pwd)
user.save(updated_fields=["password"])
return user
# or you could replace it in validated_data
def create(self, validated_data):
validated_data["password"] = make_password(validated_data["password"])
return User.objects.create(**attrs)
You have a validation routine, but it is global. If you make it specific to the password field then you can calculate it there and leave create alone. People don't usually do that, because it is standard to have a "confirm password" field as well.
def validate_password(self, value):
# ...
return make_password(value)
Now if you want to add a confirm_password field then you are going to need to a validate(self, attrs) anyway to compare the two fields, so I would recommend against this last method.

django.db.utils.IntegrityError: UNIQUE constraint failed: authentication_user.email

I am trying create user through an API, But i am struck on above error.
Below are the code of the User and its manager. Here, I am creating custom user model.
class UserManager(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)
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 none')
user = self.create_user(username, email, password)
user.save()
return user
class User(AbstractBaseUser,PermissionsMixin):
username = models.CharField(max_length=255, unique=True, db_index=True)
email = models.EmailField(max_length=255,unique=True,db_index=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects= UserManager()
def __str__(self):
return self.email
Below is serializers.py file.
class RegisterSerializers(serializers.ModelSerializer):
password = serializers.CharField(max_length=68, min_length=6, write_only=True)
class Meta:
model = User
fields = ['email','username','password']
def validate(self, attrs):
email = attrs.get('email','')
username = attrs.get('username','')
if not username.isalnum():
raise serializers.ValidationError('The username should only contain alphanumeric character')
return attrs
def create(self, validated_data):
return User.objects.create_user(**validated_data)
Here is POST request in views.py
class RegisterView(generics.GenericAPIView):
serializer_class = RegisterSerializers
def post(self, request):
user = request.data
serializer = self.serializer_class(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
user_data = serializer.data
return Response(user_data,status=status.HTTP_201_CREATED)
I am new to drf. Kindly help me out, thanks.

RegisterForm() missing 1 required positional argument: 'request'

So I'm making a custom user model. This is what I'am following Here. I have been pretty much following the tutorial but still I cant make it done.
Error: RegisterForm() missing 1 required positional argument: 'request'.
here's my code.
forms.py
from django import forms
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from .models import User
class UserAdminCreationForm(forms.ModelForm):
"""
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email',)
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('email', 'password', 'active', 'admin')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial["password"]
class LoginForm(forms.ModelForm):
email = forms.EmailField(label='Email')
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email', 'password',)
widgets = {
'email' : forms.EmailInput(
attrs={'class':'form-control', 'place_holder': '', }),
'password' : forms.PasswordInput(
attrs={'class':'form-control' }),
}
class RegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email',)
def clean_email(self):
email = self.cleaned_data.get('email')
qs = User.objects.filter(email=email)
if qs.exists():
raise forms.ValidationError("email is taken")
return email
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return password2
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, password=None, is_staff=False, is_active=True, is_admin=False):
"""
Creates and saves a User with the given email and password.
"""
if not email:
raise ValueError('Users must have an email address')
if not full_name:
raise ValueError('Users must have an full name')
if not password:
raise ValueError('Users must have a password')
user = self.model(
email=self.normalize_email(email),
)
user.full_name = full_name
user.set_password(password)
user.staff = is_staff
user.admin = is_admin
user.active = is_active
user.save(using=self._db)
return user
def create_staffuser(self, email, password):
"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
email,
password=password,
)
user.staff = True
user.save(using=self._db)
return user
def create_superuser(self, email, full_name, password):
"""
Creates and saves a superuser with the given email and password.
"""
user = self.model(
email=self.normalize_email(email)
)
user.full_name = full_name
user.set_password(password)
user.full_name = full_name
user.staff = True
user.admin = True
user.save(using=self._db)
return user
# Create your models here.
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
full_name = models.CharField(max_length=255, null=True, blank=True)
active = models.BooleanField(default=True) # to login
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
created_date = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['full_name'] # Email & Password are required by default.
objects = UserManager()
def __str__(self):
return self.email
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 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.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
class Account_type(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
account_type = models.ForeignKey(Account_type, on_delete=models.CASCADE)
register.html
from django.shortcuts import render, redirect
from . forms import RegisterForm, LoginForm
# Create your views here.
def RegisterForm(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
else:
form = RegisterForm()
context = {
'form' : form
}
return render(request, 'account/register.html', context)
The view logic is simple as you can see. Just saving up the request into the database. The tutorial itself did not tell anything about the view for login and register.
So, What am I doing wrong here.
Thank you
The problem is that your view RegisterForm has the same name as your form, hence if you call RegisterForm in your view, it will resolve to the view function, and make a recursive call.
Normally (top-level) functions are written in snake_case, hence you can rewrite it to register_form, or even better register (since it is not a form at all):
from django.shortcuts import render, redirect
from . forms import RegisterForm, LoginForm
# Create your views here.
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
return redirect('some-view-name')
else:
form = RegisterForm()
context = {
'form' : form
}
return render(request, 'account/register.html', context)
Normally a successful POST request results in a redirect to implement the Post/Redirect/Get pattern [wiki]. So I strongly advise you to use redirect(..) [Django-doc] and replace some-view-name with the name of a view to which you want to redirect.

django admin not working with custom user

I feel like I'm missing somehting obvious on this one.
I've created a custom user and user manger
class UserManager(BaseUserManager):
# create a normal user
# an email and password must be provided
def create_user(self, email, password, first_name, last_name,
location, date_of_birth):
if not email:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
email = email.lower()
user = self.model(
email=email,
first_name=first_name,
last_name=last_name,
location=location,
date_of_birth=date_of_birth
)
user.set_password(password)
user.save(using=self._db)
return user
# Make an administrator
def create_superuser(self, email, password, first_name, last_name,
location, date_of_birth):
user = self.create_user(
email=email,
password=password,
first_name=first_name,
last_name=last_name,
location=location,
date_of_birth=date_of_birth
)
user.is_admin = True
user.is_moderator = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True
)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
location = models.ForeignKey(Location)
date_of_birth = models.DateField()
date_joined = models.DateTimeField(auto_now_add=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_moderator = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name', 'location', 'date_of_birth']
def __unicode__(self):
return self.email
def get_full_name(self):
return self.first_name + ' ' + self.last_name
def get_age(self):
age = date.today() - self.date_of_birth
return age.days / 365
def is_staff(self):
return self.is_admin
def has_perm(self, perm, obj=None):
return True
def has_module_perms(self, app_label):
return True
However if I visit the admin site, It will happily authorize a user who is not an admin is_admin=False
Has anyone run into this problem, Is there something I need to change when using django admin with a custom user?
EDIT
setting.py
AUTH_USER_MODEL = 'userAccount.User'
AUTHENTICATION_BACKEND = (
'django.contrib.auth.backends.ModelBackend',
)
is_admin is not something that django's authentication system knows about. In the authentication form that is used, it is only checking if the user is active or is staff:
class AdminAuthenticationForm(AuthenticationForm):
"""
A custom authentication form used in the admin app.
"""
error_messages = {
'invalid_login': _("Please enter the correct %(username)s and password "
"for a staff account. Note that both fields may be "
"case-sensitive."),
}
required_css_class = 'required'
def confirm_login_allowed(self, user):
if not user.is_active or not user.is_staff:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name}
)
In the original user model, is_staff is a model field. You do not have such a field, but rather a method. This could be a why its not working.
You can solve this problem two ways:
Create your own AdminAuthenticationForm and adjust the confirm_login_allowed method to check for is_admin rather than is_staff.
Create a is_staff property in your custom user model:
#property
def is_staff(self):
return self._is_admin