Custom Register Form with Django Rest Auth - Error: save() takes 1 positional argument but 2 were given - django

I want to create a custom Signup form with Django Rest Auth and Django Allauth but I'm getting an error save() takes 1 positional argument but 2 were given
I know that this error is related to the function def save(self, request) provided by Django Rest Auth, but I have no clue how I can change it.
What should I do to make it work?
Bellow is the respective code for my user Model and Serializer:
# Models.py
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, **extra_fields):
"""Creates and saves a new super user"""
user = self.create_user(email, password, **extra_fields)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model that supports using email instead of username"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
age = models.PositiveIntegerField(null=True, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'age']
def __str__(self):
return self.email
# serializers.py
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the users object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name', 'age')
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def create(self, validated_data):
"""Create a new user with encrypted password and return it"""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update a user, setting the password correctly and return it"""
password = validated_data.pop('password', None)
user = super().update(instance, **validated_data)
if password:
user.set_password(password)
user.save()
return user
def save(self, request):
# I would say that I need to change the default function
# settings. py
REST_AUTH_REGISTER_SERIALIZERS = {
'REGISTER_SERIALIZER': 'user.serializers.UserSerializer'
}

In order to get this fixed I just add the following to my serializer:
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the users object"""
class Meta:
model = get_user_model()
fields = ('email', 'password', 'name', 'age')
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
def create(self, validated_data):
"""Create a new user with encrypted password and return it"""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update a user, setting the password correctly and return it"""
password = validated_data.pop('password', None)
user = super().update(instance, **validated_data)
if password:
user.set_password(password)
user.save()
return user
def get_cleaned_data(self):
return {
'password': self.validated_data.get('password', ''),
'email': self.validated_data.get('email', ''),
'name': self.validated_data.get('name', ''),
'age': self.validated_data.get('age', ''),
}
def save(self, request):
self.cleaned_data = self.get_cleaned_data()
user = get_user_model().objects.create_user(**self.cleaned_data)
return user

Related

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.

Django Authentication register API

I am trying to register users in django auth module through an API call but the users are getting registered without the password being hashed which, I suspect, is making my authentication fail. Registering the users through the admin form is hashing the password and therefore working.
I developed my own User model by extending AbstractBaseUser and also created a UserManager extending BaseUserManager and defining the create_user and create_superuser method. I developed a simple serializer for it.
I read somewhere that the password can only be hashed if I developed the Admin form as well and so I did it. In this form, I followed django documentation and developed clean_password and save functions. I also registered these forms on the app admin.py.
Lastly, I created the APIView to the POST requests where I send the registration json and use the serializer do validate and save.
model
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('The given email must be set')
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, **extra_fields):
user = self.create_user(email,
password=password,
**extra_fields)
user.is_admin = True
user.save(using=self._db)
return user
class User(AbstractBaseUser):
email = models.EmailField(max_length=40, unique=True)
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
photo_path = models.CharField(max_length=30, blank=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
def save(self, *args, **kwargs):
super(User, self).save(*args, **kwargs)
return self
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
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):
return self.is_admin
serializer
class UserSerializer(serializers.ModelSerializer):
class Meta(object):
model = User
fields = ('id', 'email', 'first_name', 'last_name', 'password')
extra_kwargs = {'password': {'write_only': True}}
forms
class UserCreationForm(forms.ModelForm):
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('email', 'photo_path')
def clean_password2(self):
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):
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
class Meta:
model = User
fields = ('email', 'photo_path', 'password')
def clean_password(self):
return self.initial["password"]
admin.py
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('email', 'first_name', 'is_staff')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('first_name',)}),
('Permissions', {'fields': ('is_admin',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')}
),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
view post
class CreateUserAPIView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
user = request.data
serializer = UserSerializer(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
I expected to get a user in the DB with a hashed password, like when I create a user in the admin panel. but I get a user created with a plain text password.
What I would do, is the following in your serializer. Notice the set_password. That way you make sure it is hashed
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = models.User
fields = ('username', 'password', 'email')
def create(self, validated_data):
user = super(UserSerializer, self).create(validated_data)
user.set_password(validated_data['password'])
user.save()
return user
If you are using md5 for hashing then you can use the hashlib module and hash the password before saving in create_superuser
form hashlib import md5
def create_superuser(self, email, password, **extra_fields):
user = self.create_user(email,password=md5(password),**extra_fields)
user.is_admin = True
user.save(using=self._db)
return user
Sorry for the quick auto-response but I found out that the view post code was actually not executing my model create_user code. I don't know what was connecting the .save() method of the serializer to the authentication system but it was still creating the users. I will leave this question open so someone can maybe explain what was happening. To make it work i did the following changes:
class CreateUserAPIView(APIView):
permission_classes = (AllowAny,)
def post(self, request):
user = User.objects.create_user(request.data['email'], request.data['password']);
return Response(user, status=status.HTTP_201_CREATED)

django AttributeError: dict object has no attribute 'pk'

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.

How to set permissions for an inherited User model from AbstractBaseUser with Django restframework

lets have a structure of my User model.
BaseUser <- BusinessOwner <- Business Staff (BusinessOwner can create,update,delete BusinessStaff )
BaseUser <- Customer
I'm having problem when creating an inherited user from AbstractBaseUser, All i want is to add a row in myappname_baseuser_user_permissions table which has columns : id , baseuser_id, permission_id, with correct permissions assign to correct user id, such as : add_staff, change_staff, delete_staff
Things is normal when i create new BusinessOwner user with admin page and easily add permissions, but not the POST method im using with django restframework, there are no permissions. So where should i put my permissions code? and what is the code? in model.py or view.py ? ,my permissions codename are : add_staff, change_staff, and delete_staff
here is my model.py:
class UserManager(BaseUserManager):
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
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 BaseUser(AbstractBaseUser, PermissionsMixin):
first_name=models.CharField(max_length=20)
last_name=models.CharField(max_length=20)
email=models.EmailField(max_length=254, unique=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
class BusinessOwner(BaseUser):
business_name=models.CharField(max_length=20)
class Customer(BaseUser):
address=models.CharField(max_length=30)
class Staff(BaseUser):
position=models.CharField(max_length=30)
my view.py
class CreateBusinessOwnerView(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = BusinessOwner.objects.all()
serializer_class = CreateBusinessOwner
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def post_save(self, obj, created=False):
if created:
obj.set_password(obj.password)
obj.save()
and my serializers.py
class CreateBusinessOwner(serializers.ModelSerializer):
class Meta:
model = BusinessOwner
fields = ('email', 'password', 'first_name', 'last_name', 'business_name' ,'date_joined')
read_only_fields = ('date_joined',)
Thanks for helping me!