I created custom user models as so:
class UserAccount(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name']
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def __str__(self):
return self.email
manager:
class UserAccountManager(BaseUserManager):
def create_user(self, email, name, password=None):
if not email:
raise ValueError('Users must have unique email address')
email = self.normalize_email(email)
user = self.model(email=email, name=name)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, name, password):
user = self.create_user(email, name, password)
user.is_superuser = True
user.is_staff = True
user.save()
return user
I have called the user.set_password(password) in the manager, yet it is not hashing it. For this, I can't login! Here is the view that I used for the signup process:
class SignupView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
data = self.request.data
name = data['name']
email = data['email']
password = data['password']
password2 = data['password2']
if password == password2:
if User.objects.filter(email=email).exists():
return Response({'error': 'Email already exists'})
if len(password) < 6:
return Response({'error': 'Password must be more than 6 characters in length'})
user = User.objects.create(email=email, password=password, name=name)
user.save()
return Response({'success': 'User created successfully'})
return Response({'error': 'Passwords do not match'})
I have seen other solutions but they were related to serializers. In my case I have not used any serializer. How do i fix this?
You should not call .create(…), but .create_user(…):
class SignupView(APIView):
permission_classes = (permissions.AllowAny,)
def post(self, request, format=None):
data = self.request.data
name = data['name']
email = data['email']
password = data['password']
password2 = data['password2']
if password == password2:
if User.objects.filter(email=email).exists():
return Response({'error': 'Email already exists'})
if len(password) < 6:
return Response({'error': 'Password must be more than 6 characters in length'})
user = User.objects.create_user(email=email, password=password, name=name)
user.save()
return Response({'success': 'User created successfully'})
return Response({'error': 'Passwords do not match'})
Related
when i am sending post request to register new user from postman, username saves as blank/dash .
why username saving as blank i dont get it.
but when i add from django admin it saves correctly.
here is postman image
postman post request image
and here is the result
django result after sending post request
Custom Account Manager
class CustomAccountManager(BaseUserManager):
def create_superuser(self, email, username, password, **other_fields):
other_fields.setdefault('is_staff', True)
other_fields.setdefault('is_active', True)
other_fields.setdefault('is_admin', True)
other_fields.setdefault('is_superuser', True)
if other_fields.get('is_staff') is not True:
raise ValueError(
'Superuser must be assigned to is_staff=True.')
if other_fields.get('is_superuser') is not True:
raise ValueError(
'Superuser must be assigned to is_superuser=True.')
return self.create_user(email, username, password, **other_fields)
def create_user(self, email, username, password, **other_fields):
if not email:
raise ValueError(_('You must provide an email address'))
if not username:
raise ValueError(_('You must provide an username'))
if " " in username:
raise ValueError(_('Username should not contain space'))
email = self.normalize_email(email)
user = self.model(email=email, username=username, **other_fields)
user.set_password(password)
user.save()
return user
Custom User Model
class NewUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True, null=False, blank=False)
username = models.CharField(max_length=150, unique=True, null=True)
first_name = models.CharField(max_length=150, blank=True)
objects = CustomAccountManager()
# more
class Meta:
verbose_name = 'NewUser'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __str__(self):
return str(self.username)
model serializer
class RegisterNewUserSerializer(serializers.ModelSerializer):
class Meta:
model = NewUser
fields = ('email', 'password', 'username')
extra_kwargs={'password':{'write_only':True}}
def validate(self, data):
if hasattr(self, 'initial_data'):
unknown_keys = set(self.initial_data.keys()) - set(self.fields.keys())
if unknown_keys:
raise ValidationError("Got unknown fields: {}".format(unknown_keys))
return data
def validate_username(self, value):
if " " in value:
raise serializers.ValidationError("Username should not contain space")
def save(self):
newuser = NewUser(email = self.validated_data['email'],
username = self.validated_data['username']
)
password = self.validated_data['password']
newuser.set_password(password)
newuser.save()
return newuser
post request
class RegistrationView(APIView):
permission_classes = [ AllowAny ]
def post(self, request):
reg_serializer = RegisterNewUserSerializer(data = request.data)
print(request.data)
print(reg_serializer)
if reg_serializer.is_valid():
print('lololololololololololo')
reg_serializer.save()
return Response(data=request.data,status=status.HTTP_201_CREATED)
return Response(reg_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
You validator should return the data, so:
def validate_username(self, value):
if " " in value:
raise serializers.ValidationError("Username should not contain space")
# ↓ return the value
return value
In Python a function call always returns something (unless an exception is raised). If no explicit return statement is triggered, None is returned.
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.
I followed CodingEntrepreneurs tutorial on Custom Users and extended it to have 3 types of users: school, parent and vendor.
I have created view, form for each type of user.
In forms.py I have
class ParentSignUpForm(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):
user = super(ParentSignUpForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.is_parent = True
user.save()
return user
I try to change is_parent attribute from False to True. However, error is:
File "/home/azamat/dev/dj_test/src/accounts/forms.py", line 137, in save
user.is_parent = True
AttributeError: can't set attribute
How to solve this issue? Or any better way to save users with different types?
UPDATE:
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager
)
class UserManager(BaseUserManager):
def create_user(self, email, password=None, is_staff=False, is_admin=False, is_active=True, is_parent=False, is_school=False, is_vendor=False):
if not email:
raise ValueError("Users must have an email address")
if not password:
raise ValueError("Users must have a password")
user_obj = self.model(
email = self.normalize_email(email)
)
user_obj.set_password(password) # change user password
user_obj.parent = is_parent
user_obj.school = is_school
user_obj.vendor = is_vendor
user_obj.active = is_active
user_obj.staff = is_staff
user_obj.admin = is_admin
user_obj.save(using=self._db)
return user_obj
def create_parentuser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_parent=True
)
return user
def create_schooluser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_school=True
)
return user
def create_vendoruser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_vendor=True
)
return user
def create_staffuser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_staff=True
)
return user
def create_superuser(self, email, password=None):
user = self.create_user(
email,
password=password,
is_staff=True,
is_admin=True,
)
return user
class User(AbstractBaseUser):
email = models.EmailField(max_length=255, unique=True)
active = models.BooleanField(default=True) # can login
parent = models.BooleanField(default=False) # parent user
school = models.BooleanField(default=False) # school admin user
vendor = models.BooleanField(default=False) # vendor user
staff = models.BooleanField(default=False) # staff user non superuser
admin = models.BooleanField(default=False) # superuser
timestamp = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email' #username
objects = UserManager()
def __str__(self):
return self.email
def get_full_name(self):
return self.email
def get_short_name(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_parent(self):
return self.parent
#property
def is_school(self):
return self.school
#property
def is_vendor(self):
return self.is_vendor
#property
def is_active(self):
return self.active
#property
def is_staff(self):
return self.staff
#property
def is_admin(self):
return self.admin
How I solved it:
I have added REQUIRED_FIELDS = ['parent', 'school', 'vendor' ] to my model.py So now on my registration page, I have 3 check buttons where I can select a needed type of user.
In forms.py I left only RegisterForm and deleted register forms for my 3 user types
class RegisterForm(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', **'parent', 'school', 'vendor'**) #'added 3 fields',)
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(RegisterForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
# user.active = False # send confirmation email
if commit:
user.save()
return user
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.
I'm having trouble while sending PUT request for my custom user model
On sending PUT request to /api/provider/1/, where 1 is pk, with put data phone_number = 1234567890 I get {"email": ["This field is required."] }
If I send email and phone_number, I get {"email": ["Provider with this email already exists."]}
Model
class ProviderManager(BaseUserManager):
def _create_user(self, email, password, is_staff, is_superuser, **extra_fields):
'''Creates and saves a User with the given email and password'''
now = timezone.now()
if not email:
raise ValueError('Email can\'t be empty')
email = self.normalize_email(email)
user = self.model(email=email, is_active=True, is_superuser=is_superuser,
is_staff=is_staff, 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, phone_number, **extra_fields):
extra_fields['phone_number'] = phone_number
return self._create_user(email, password, False, False, **extra_fields)
def create_user(self, email, password, phone_number, **extra_fields):
extra_fields['phone_number'] = phone_number
return self._create_user(email, password, True, True, **extra_fields)
class Provider(AbstractBaseUser):
name = models.CharField(max_length=256, default='')
email = models.EmailField(max_length=256, unique=True, db_index=True)
phone_number = models.CharField(max_length=32, unique=True)
language = models.CharField(max_length=8, default='')
currency = models.CharField(max_length=8, default='')
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['phone_number',]
objects = ProviderManager()
class Meta:
verbose_name = 'user'
verbose_name_plural = 'users'
def __unicode__(self):
return self.email
def get_full_name(self):
return self.name
def get_short_name(self):
return self.name
def email_user(self, subject, message, from_email=None, **kwargs):
send_mail(subject, message, from_email, [self.email])
Serializer
class ProviderSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('id', 'name', 'email', 'phone_number', 'language', 'currency',)
View
#api_view(['GET', 'PUT', 'DELETE'])
def provider_detail(request, provider_id):
'''Read, update or delete a provider'''
try:
provider = User.objects.get(pk=provider_id)
except User.DoesNotExist:
return Response(status=HTTP_404_NOT_FOUND)
print('GOT PROVIDER ' + provider.email)
if request.method == 'GET':
serializer = ProviderSerializer(provider)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = ProviderSerializer(provider, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
provider.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
When doing a PUT you need to submit all the fields for the model only a PATCH would allow you to update by passing one field
There is partial parameter which you can pass to serializer, to indicate, that it is a partial update.
From the doc:
http://www.django-rest-framework.org/api-guide/serializers/#partial-updates