Invalid username/password error django rest framework custom user serializer - django

Custom User Model:
class User(AbstractUser):
ROLE_CHOICES = (
('R', 'rider'),
('D', 'driver'),
)
role = models.CharField(max_length=1, choices=ROLE_CHOICES)
phone_number = models.CharField(max_length=10)
cab = models.OneToOneField('Cab', on_delete=models.CASCADE, blank=True, null=True)
Rider serializer:
class RiderSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'phone_number', 'password')
extra_kwargs = {
'password': {'write_only': True}
}
def create(self, validated_data):
username = validated_data.pop('username')
password = validated_data.pop('password')
instance = User(username, **validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
Rider function based view method:
#api_view(['GET', 'POST'])
def rider_list(request):
if request.method == 'GET':
riders = User.objects.filter(role='R')
serializer = RiderSerializer(riders, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = RiderSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(role='R')
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
rider endpoint: /riders/
I am able to create a user object but user authentication fails as password is getting stored as plain text in object.
I have tried using User.objects.create_user(username, password=password, **validated_data) to set password as hashed value but it does not work
I have also tried using make_password method to set hashed password but nothing seems to work.
Please tell me what am i missing. How do i store the hashed password in password field of custom user object.

create() method should be part of serializer class, not part of Meta:
class RiderSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'phone_number', 'password')
extra_kwargs = {
'password': {'write_only': True}
}
def create(self, validated_data):
password = validated_data.pop('password')
instance = User(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
Also you don't need to pop username field. Just pop password and use it in set_password method.

Related

simpleJWT confirm password field

I'm creating a small app that uses simpleJWT. I want the user to retype their password when registering. Currently, I'm sending data like this
{
"email":"test",
"user_name":"test3",
"password":"b12#wsqp"
}
But what I want to validate is
{
"email":"test",
"user_name":"test3",
"password":"b12#wsqp",
"password2":"b12#wsqp"
}
What i have now is
class CustomUserCreate(APIView):
permission_classes = (AllowAny,)
def post(self, request):
reg_serializer = RegisterUserSerializer(data=request.data)
if reg_serializer.is_valid():
new_user = reg_serializer.save()
if new_user:
return Response(status=status.HTTP_201_CREATED)
return Response(reg_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class RegisterUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'user_name', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
So my guess is to do
class RegisterUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'user_name', 'password1', 'password2')
extra_kwargs = {'password': {'write_only': True}, 'password2':{'write_only': True}}
def create(self, validated_data):
password1 = validated_data.pop('password1', None)
password2 = validated_data.pop('password2', None)
instance = self.Meta.model(**validated_data)
if password1 is not None and password2 is not None:
if password1 == password2:
instance.set_password(password)
else:
#return some error
instance.save()
return instance
How can I validate if the password are matching?
You can validate the two passwords fields with serializers validate(self, attrs) method like this :
def validate(self, attrs):
if attrs['password1'] != attrs['password2']:
raise serializers.ValidationError({"password": "Password fields didn't match."})
return attrs
in your case change your RegisterUserSerializer class like this :
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework.validators import UniqueValidator
from django.contrib.auth.password_validation import validate_password
class RegisterUserSerializer(serializers.ModelSerializer):
password1 = serializers.CharField(write_only=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ('email','user_name', 'password1', 'password2')
def validate(self, attrs):
if attrs['password1'] != attrs['password2']:
raise serializers.ValidationError({"password": "Password fields didn't match."})
return attrs
def create(self, validated_data):
user = User.objects.create(
username=validated_data['user_name'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.save()
return user

Django rest api does not save password

I want to make a user registration form. Everything works and the user is saved, but his password is not stored. When I see the user page in admin it is in the password field -> "Invalid password format or unknown hashing algorithm.". i use default user mode.
this is my code ->
serializers.py
class RegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password', )
extra_kwargs = {'password': {'write_only': True},}
def create(self, validated_data):
user = User(
username=validated_data['username']
)
user.set_password(validated_data['password'])
user.save()
return user
views.py
class CreateUser(generics.CreateAPIView):
permission_classes=[AllowAny]
serializer_class=RegisterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
token, _ = Token.objects.get_or_create(user=user)
return Response({
"user": RegisterSerializer(user, context=self.get_serializer_context()).data,
"token": token.key
})

Password required Django REST API User Serializer PUT request

I a the problem that in my Django Rest API User Serializer: The password field is necessary when making a PUT request. Within a POST request this makes sense, but with PUT is especially if PUT is performed by an administrator, the password field should allowed to be empty.
How can I change my serializer so that the password is necessary for POST, but empty for PUT?
class UserSerializer(serializers.ModelSerializer):
"""The UserSerializer"""
gender = serializers.IntegerField(source='profile.gender', read_only=False)
clinic = serializers.CharField(
source='profile.clinic.code', read_only=False)
title_prefix = serializers.CharField(
source='profile.academic_title_prefix',
allow_blank=True,
read_only=False)
title_suffix = serializers.CharField(
source='profile.academic_title_suffix',
allow_blank=True,
read_only=False)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())])
username = serializers.CharField(
validators=[UniqueValidator(queryset=User.objects.all())])
password = serializers.CharField(min_length=8, write_only=True)
class Meta:
model = User
fields = ('id', 'url', 'username', 'first_name', 'last_name', 'gender',
'title_prefix', 'title_suffix', 'clinic', 'email',
'is_staff', 'is_superuser', 'date_joined', 'last_login',
'password')
def create(self, validated_data):
"""Create and return a new user and its associated profile."""
user = User.objects.create_user(
validated_data['username'],
validated_data['email'],
validated_data['password'],
)
user.set_password(validated_data['password'])
user.first_name = validated_data['first_name']
user.last_name = validated_data['last_name']
user.is_staff = validated_data['is_staff']
user.is_superuser = validated_data['is_superuser']
user.save()
# create associated profile
profile_data = validated_data.pop('profile')
profile = Profile.objects.create(
user=user,
gender=profile_data['gender'],
clinic=Clinic.objects.get(code=profile_data['clinic']['code']),
academic_title_prefix=profile_data['academic_title_prefix'],
academic_title_suffix=profile_data['academic_title_suffix'],
)
user.profile = profile
return user
def update(self, instance, validated_data):
"""Update and return a existing user and its associated profile."""
instance.first_name = validated_data.get('first_name',
instance.first_name)
instance.last_name = validated_data.get('last_name',
instance.last_name)
# Only Superuser can make Superusers
if self.context['request'].user.is_superuser:
instance.is_staff = validated_data.get('is_staff',
instance.is_staff)
instance.is_superuser = validated_data.get('is_superuser',
instance.is_superuser)
profile_data = validated_data.pop('profile')
profile = Profile.objects.get(user=instance)
profile.gender = profile_data['gender']
profile.clinic = Clinic.objects.get(
code=profile_data['clinic']['code'])
profile.academic_title_prefix = profile_data['academic_title_prefix']
profile.academic_title_suffix = profile_data['academic_title_suffix']
profile.save()
instance.profile = profile
return instance
It's not problem of the serializer. Problem is that DRF requires all fields using PUT method. Method that don't require all fields is PATCH.
You need to override update method in viewset:
def update(self, request, *args, **kwargs):
partial = True # Here I change partial to True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
If you are using PUT to update the values and only want to ommit validation for password field, then you can try like this for viewset and generic views:
class UserSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super(UserSerializer, self).__init__(*args, **kwargs)
if self.context['request'].method == "PUT":
self.fields.pop('password')
# rest of the code

Serializer is_valid() is returning true though fields are empty

I Tried and run the code with function-based view and it was working perfectly then I tried to switch to modelViewSet.
Here is my code for Serializers :
UserSerializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
username = serializers.CharField(required=True)
email = email = serializers.EmailField(validators=[UniqueValidator(queryset=get_user_model().objects.all())])
phone = serializers.CharField(required=True)
full_name = serializers.CharField(required=True)
user_type = serializers.CharField(required=True)
password = serializers.CharField(write_only=True)
fields=('username', 'email', 'phone', 'full_name', 'user_type','password')
def create(self, validated_data):
user = get_user_model().objects.create(
username=validated_data['username'],
email=validated_data['email'],
phone=validated_data['phone'],
full_name=validated_data['full_name'],
user_type=validated_data['user_type']
)
user.set_password(validated_data['password'])
user.save()
return user
Here is my modelViewSet :
class RegisterView(viewsets.ModelViewSet):
queryset = ''
def create(self, request):
if request.data.get('user', dict()).get('user_type') == 'employee':
userSerializer = UserSerializer(data=request.data.get('user', dict()))
if userSerializer.is_valid(raise_exception=ValueError):
serializer = EmployeeSerializer(data=request.data)
if serializer.is_valid(raise_exception=ValueError):
serializer.create(validated_data=request.data)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.error_messages,
status=HTTP_400_BAD_REQUEST)
def get_serializer_class(self):
if self.request.data.get('user', dict()).get('user_type') == 'employee':
return EmployeeSerializer
if self.request.data.get('user', dict()).get('user_type') == 'customer':
return CustomerSerializer
if self.action == 'customer':
return CustomerSerializer
return EmployeeSerializer
Now If I dont pass username or password then is_valid for userSerializer is giving error but if I dont pass email or phone or full_name is_valid doesnt raise any exception and it remains true.
It was working correctly with Function-based view or may be I am missing something.
My concern is it should give error if any value is missing in request.data
Serializer's field should be defined as serializer's attribute instead of meta's attribute:
class UserSerializer(serializers.ModelSerializer):
username = serializers.CharField(required=True)
email = serializers.EmailField(validators=[UniqueValidator(queryset=get_user_model().objects.all())])
phone = serializers.CharField(required=True)
full_name = serializers.CharField(required=True)
user_type = serializers.CharField(required=True)
password = serializers.CharField(write_only=True)
class Meta:
model = get_user_model()
fields=('username', 'email', 'phone', 'full_name', 'user_type','password')
With your current code required=True argument has no effect.

Django rest custom user model creation fails

I have a custom user model as following:
class Librarian(models.Model):
user = models.OneToOneField(User)
phone = models.CharField(max_length=30, blank=True)
# A library has many librarians
which_library = models.ForeignKey('Library', related_name='librarians', on_delete=models.CASCADE)
I have written serializer as following:
class LibrarianSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email')
password = serializers.CharField(source='user.password')
class Meta:
model = Librarian
#fields = '__all__'
fields = ('id', 'username', 'email', 'password', 'phone', 'which_library')
def update(self, instance, validated_data):
instance.user.email = validated_data.get('user.email', instance.user.email)
instance.user.password = validated_data.get('user.password', instance.user.password)
instance.phone = validated_data.get('phone', instance.phone)
instance.which_library = validated_data.get('which_library', instance.which_library)
instance.save()
return instance
def create(self, validated_data):
print('ok')
return Librarian.objects.create(**validated_data)
It's view:
#api_view(['POST'])
def librarian(request, library_id):
"""
Create a new librarian for a specific library
"""
if request.method == 'POST':
print('View')
serializer = LibrarianSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(
serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And I am making a POST request to the corresponding URL with following JSON data:
{
"username": "sample",
"email": "sample#gmail.com",
"password": "12345678",
"phone": "12345",
"which_library": "1"
}
It throws me Cannot assign "{u'username': u'sample', u'password': u'12345678', u'email': u'sample#gmail.com'}": "Librarian.user" must be a "User" instance error.
My goal is to create a Librarian (a user must be automatically created), that's why I am sending username, password, email fields as well. What am I doing wrong?
You need to override create method and create User instance first:
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
user.set_password(user_data['password'])
user.save()
return Librarian.objects.create(user=user, **validated_data)