DRF is not hashing password - django

Here's the model:
class CustomerStatus(models.TextChoices):
ACTIVE = 'ACT', 'Active'
EXPIRED = 'EXP', 'Expired'
REVOKED = 'REV', 'Revoked'
class Customer(models.Model):
email = models.EmailField(max_length=254, unique=True)
password = models.CharField(max_length=128)
created = models.DateTimeField(auto_now_add=True)
status = models.CharField(
max_length=3, choices=CustomerStatus.choices, default=CustomerStatus.ACTIVE
)
and the serializer:
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ['email', 'password', 'created', 'status']
extra_kwargs = {'password': {'write_only': True}}
But I want to hash the password and that's not working. I check the field in Admin site and it's stored in plain text.
I've tried these:
def create(self, validated_data):
validated_data['password'] = make_password(validated_data['password'])
return super(CustomerSerializer, self).create(validated_data)
and this one:
def create(self, validated_data):
validated_data['password'] = make_password(validated_data['password'])
return super().create(validated_data)
and this one:
customer = Customer.objects.create(
email=validated_data['email'],
password=make_password(validated_data['password']),
)
return customer
I'm running out of options.
EDIT 1
If I override save method and hash the password everything is ok. So I should not expect this to work in create method of the serializer. Correct?

Related

DRF nested serializer issue on create

I have a User model and a phone model the phone model has a foreign key relation with the user model I read the docs on NestedSerializers and tried it to no avail.
The model
class Phone(models.Model):
phone = models.CharField(
_("phone number"), max_length=13, blank=True, null=True)
otp = IntegerRangeField(min_value=6, max_value=6, blank=True, null=True)
def __str__(self):
return self.phone
class User(AbstractBaseUser, PermissionsMixin):
phone = models.ForeignKey(
Phone, on_delete=models.CASCADE, related_name="userphone", blank=False, null=True)
this is my serializers.py file
class PhoneSerializer(serializers.ModelSerializer):
# opt = serializers.IntegerField(read_only=True)
class Meta:
model = Phone
fields = ['phone']
class RegisterSerializerBase(serializers.ModelSerializer):
phone = PhoneSerializer(allow_null=True)
# otp = PhoneSerializer(many=True, read_only=True)
email = serializers.EmailField(
required=True,
validators=[UniqueValidator(queryset=User.objects.all())])
password = serializers.CharField(
write_only=True, re
quired=True, validators=[validate_password])
password2 = serializers.CharField(write_only=True, required=True)
class Meta:
model = User
fields = ('email', 'firstname', 'lastname', 'password', 'password2',
'phone',)
extra_kwargs = {'password': {'write_only': True},
'password2': {'write_only': True},
'firstname': {'required': True},
'lastname': {'required': True},
}
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError(
{"password": "Password fields didn't match."})
return attrs
def create(self, validated_data):
phones = validated_data.pop('phone')
instance = User.objects.create(
email=validated_data['email'],
firstname=validated_data['firstname'],
lastname=validated_data['lastname'],
is_active='True',
type=User.TYPES.OWNER,
)
instance.set_password(validated_data['password'])
for phone in phones:
Phone.objects.create(instance=instance, **phone)
instance.save()
return instance
when I go the the Browsable API and create a user with the credentials an error comes up saying
django.db.models.manager.BaseManager._get_queryset_methods.<locals>.create_method.<locals>.manager_method() argument after ** must be a mapping, not str
Check if this helps you -
Models -
User model is already defined.
class Phone(models.Model):
user = models.Foerienkey(User, related_name="abc")
phone = models.CharField(max_length=13, blank=True, null=True)
Serializer -
from rest_framework import serializers
from django.contrib.auth.models import User
class CurrentUserSerializer(serializers.Serializer):
abc = phoneserializer(many=True, required=False)
## define phone serializer before currentuser serializer
class Meta:
model = User
fields = ('username', 'email', 'id', 'abc')
Views -
##user create method
def create(self, validated_data):
phones = validated_data.pop('recievedPhoneDataArray')
instance = User.objects.create(
email=validated_data['email'],
firstname=validated_data['firstname'],
lastname=validated_data['lastname'],
is_active='True',
type=User.TYPES.OWNER,
)
instance.set_password(validated_data['password'])
for phone in phones:
Phone.objects.create(user=instance, **phone)
instance.save()
return instance
You will have to extend the registeruserserializer from rest frameowrk. may be.

How to add a foreign key to the username. Django REST framework

During serialization, i noticed that the post_author Foreign Key of the Post model is referencing the id of the creator and thus, i can't display the username of the creator in the REST API, only the post_author id.
How can i add the username of the post_creator, so that it is readable to other users, when i fetch the data on the frontend?
models.py // CustomUser = the Creator of the post.
class CustomUser(AbstractUser):
fav_color = models.CharField(blank=True, max_length=120)
class Post(models.Model):
post_author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='posts')
post_title = models.CharField(max_length=200)
post_body = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.post_title
views.py
#api_view(['GET'])
def post_list(request):
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data)
serializers.py user model and post model serialization
class CustomUserSerializer(serializers.ModelSerializer):
"""
Currently unused in preference of the below.
"""
email = serializers.EmailField(required=True)
username = serializers.CharField()
password = serializers.CharField(min_length=8, write_only=True)
class Meta:
model = CustomUser
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
# as long as the fields are the same, we can just use this
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
single post from the API
{
"id": 1,
"post_title": "first_post",
"post_body": "qwe1",
"created_date": "2020-11-17T19:30:55Z",
"published_date": null,
"post_author": 1
},
You need to override the serializer:
class PostSerializer(serializers.ModelSerializer):
post_author_username = serializers.ReadOnlyField(source="post_author.username")
class Meta:
model = Post
fields = [post_author_username, post_title, post_body, created_data, published_data]
You can to specify the post_author serializer in your PostSerializer:
class PostSerializer(serializers.ModelSerializer):
post_author=CustomUserSerializer(read_only=True)
class Meta:
model = Post
fields = '__all__'
You can see the documentation here

How to get user instance in Model Serializer in django Rest Framework

I have 2 models CustomUser and Student. In CustomUserSerializer, whenever the user is created i also want to create Student instance inside CustomUserSerializer. How can I get the current user instance?
# models.py
class CustomUser(AbstractUser):
is_student = models.BooleanField('student status', default=False)
is_teacher = models.BooleanField('teacher status', default=False)
email = models.EmailField(_('Email Address'), unique=True, blank=True)
class Student(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True)
# serializers.py
class CustomUserSerializer(serializers.ModelSerializer):
class Meta:
model = models.CustomUser
fields = [
'id', 'email', 'is_teacher', 'is_student',..]
def create(self, validated_data):
if validated_data.get('is_student') == True:
Student.objects.create(user=...?) # what should I put here?
return super(CustomUserSerializer, self).create(validated_data)
[UPDATED]
I have another question it's related to Student model.
If I have models like below should I create serializer for Student? when I want to have serializer for Class.
class Class(models.Model):
student = models.ManyToManyField(Student)
Thanks
Change your create method as
class CustomUserSerializer(serializers.ModelSerializer):
def create(self, validated_data):
is_student = validated_data.pop("is_student", False)
user = super().create(validated_data)
if is_student:
Student.objects.create(user=user)
return user
class Meta:
model = models.CustomUser
fields = [
'id', 'email', 'is_teacher', 'is_student',..
]

Adding fields to User model

I am managing to use djangocontrib.auth.models.User. I need to add phone number and profile pic field to the user model.
My serializers.py:
class UserSerializer(ModelSerializer):
password = serializers.CharField(write_only=True)
phone_number = serializers.CharField()
profile_pic = serializers.ImageField(max_length=None, use_url=True, allow_null=True, required=False)
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
phone_number=validated_data['phone_number'],
profile_pic=validated_data['profile_pic'],
)
return user
class Meta:
model = User
fields = ('first_name', 'last_name','username', 'password', 'phone_number', 'profile_pic',)
But on creating user it returns 'phone_number' is an invalid keyword argument for this function, I need help to create user with phone number and profile. Thanks in advance

How to Remove validation from left table data in django

2nd Screenshot of APIAPI Sample Screenshot
I'm New in Django, i want to help regarding validations in screenshot there is company_name, location, title and user_location fields except user info with proper validation
but i want to remove validations from company_name, location, title and user_location fields how to do?
Please find the above api screenshot and
Please find the below scripts,
views.py
class UserRegistrationView(generics.CreateAPIView):
"""
Register a new user.
"""
queryset = User.objects.all()
permission_classes = (permissions.AllowAny, )
def get_serializer_class(self, user_type=None):
if user_type == 'student':
return StudentRegistrationSerializer
return ProfessionalRegistrationSerializer
def post(self, request, user_type=None, format=None):
serializer_class = self.get_serializer_class(user_type)
serializer = serializer_class(data=request.data, context={'request': request})
if serializer.is_valid(raise_exception=True)
user = serializer.save(work_status=user_type)
token, created = Token.objects.get_or_create(user=user)
data = BasicUserSerializer(user, context={'request': request}).data
data.update({"token": token.key})
return Response(data)
serializes.py
class ProfessionalRegistrationSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(max_length=20, write_only=True)
experiences = ExperienceSerializer(required=False)
email = serializers.EmailField()
first_name = serializers.CharField(max_length=30)
last_name = serializers.CharField(max_length=30)
class Meta:
model = User
fields = ('url', 'id', 'first_name', 'last_name', 'email', 'password',
'experiences', 'headline')
def validate_email(self, value):
from validate_email_address import validate_email
if User.all_objects.filter(email=value.lower()).exists():
raise serializers.ValidationError('User with this email already exists.')
# if not validate_email(value.lower(), check_mx=True):
# raise serializers.ValidationError('It looks like you may have entered an incorrect email address.')
return value.lower()
def create(self, validated_data):
experiences = validated_data.pop('experiences')
password = validated_data.pop('password')
email = validated_data.pop('email')
user = User.objects.create(
username=email.lower(),
email=email.lower(),
role_id=1)
user.set_password(password)
user.save()
user_location = experiences.pop('user_location')
if hasattr(user, 'location'):
user.location.location = user_location
user.save()
else:
UserLocation.objects.create(user=user, location=user_location)
Experience.objects.create(user=user)
return user
Another serializes.py for Experiance
class ExperienceSerializer(serializers.HyperlinkedModelSerializer):
user_location = LocationField()
location = LocationField()
class Meta:
model = Experience
fields = ('id', 'company_name', 'company', 'description', 'location',
'title', 'start_date', 'end_date', 'is_current', 'user_location')
I want to Remove Validation from company_name, company, description, location, title, start_date, end_date, user_location
actually these fields are second page means after complete the first step users move on second step so second step fields are optional
class ExperienceSerializer(serializers.HyperlinkedModelSerializer):
user_location = LocationField()
location = LocationField()
class Meta:
model = Experience
fields = ('id', 'company_name', 'company', 'description', 'location',
'title', 'start_date', 'end_date', 'is_current', 'user_location')
def create(self, validated_data):
return Experience.objects.create(field_a='value', field_b='value')
in the above class, what should be do to remove validation of
"error_msg": {
"location": [
"Expected a dictionary of items but got type \"str\"."
],
"start_date": [
"Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]."
],
"end_date": [
"Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]]."
],
"user_location": [
"Expected a dictionary of items but got type \"str\"."
]
}
Experience Model
class Experience(models.Model):
"""
"""
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='experiences')
company_name = models.CharField(max_length=200, db_index=True, blank=True)
company = models.ForeignKey('organisations.Organisation', null=True, blank=True, on_delete=models.SET_NULL)
description = models.TextField(null=True, blank=True)
location = models.ForeignKey('regions.Location', null=True, blank=True, on_delete=models.SET_NULL)
start_date = models.DateField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
title = models.CharField(max_length=200, db_index=True, blank=True)
is_current = models.BooleanField(default=False)
is_associated = models.BooleanField(default=False)
created_at = models.DateTimeField(_('created at'), auto_now_add=True)
modified_at = models.DateTimeField(_('modified at'), auto_now=True)
class Meta:
db_table = 'experience'
verbose_name = _('experience')
verbose_name_plural = _('experiences')
ordering = ('-start_date',)
def __str__(self):
return getattr(self, 'title', '')
#property
def experience(self):
if self.end_date:
return (self.end_date - self.start_date).days
else:
return (datetime.datetime.now().date() - self.start_date).days
def get_formated_experience(self):
days = self.experience
total_months = round(days/30)
years = int(total_months/12)
months = round(((total_months/12)%1)*12)
year_txt = 'years' if years > 1 else 'year'
month_txt = 'months' if months > 1 else 'month'
return "%s %s %s %s" %(years, year_txt, months, month_txt)
Location Model
class Location(models.Model):
"""
"""
id = models.TextField(primary_key=True)
display_name = models.TextField(null=True, blank=True)
latitude = models.DecimalField(max_digits=15, decimal_places=10, null=True, blank=True)
longitude = models.DecimalField(max_digits=15, decimal_places=10, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
objects = LocationManager()
You are getting Two types of validation error according to snapshot.
Field is required
Expected a dictionary and got a string
The required field error occurs when you have set field as required in your model. You can change this by adding blank=True in your model for that field.
For second error, your serializer is expecting a dictionary and you are sending a string. You can remove this validation by writing your custom create method.
class ExperienceSerializer(serializers.HyperlinkedModelSerializer):
user_location = LocationField()
location = LocationField()
class Meta:
model = Experience
fields = ('id', 'company_name', 'company', 'description', 'location',
'title', 'start_date', 'end_date', 'is_current', 'user_location')
def create(self, validated_data):
# you create code for that models.
Your seriailzers will be like this
class ProfessionalRegistrationSerializer(serializers.HyperlinkedModelSerializer):
password = serializers.CharField(max_length=20, write_only=True)
experiences = ExperienceSerializer(required=False)
email = serializers.EmailField()
first_name = serializers.CharField(max_length=30)
last_name = serializers.CharField(max_length=30)
class Meta:
model = User
fields = ('url', 'id', 'first_name', 'last_name', 'email', 'password',
'experiences', 'headline')
def validate_email(self, value):
from validate_email_address import validate_email
if User.all_objects.filter(email=value.lower()).exists():
raise serializers.ValidationError('User with this email already exists.')
# if not validate_email(value.lower(), check_mx=True):
# raise serializers.ValidationError('It looks like you may have entered an incorrect email address.')
return value.lower()
def create(self, validated_data):
experiences = validated_data.get('experiences')
password = validated_data.get('password')
email = validated_data.get('email')
user = User.objects.create(
username=email.lower(),
email=email.lower(),
role_id=1)
user.set_password(password)
user.save()
user_location = experiences.get('user_location')
location_object = None
if user_location:
location_object, created = Location.objects.get_or_create(display_name=user_location.get('display_name'), latitude= user_location.get('latitude'), longitude=user_location.get('longitude'))
user_experience = Experience.objects.create(user=user, company_name=experiences.get('company_name'), location=location_object)
return user
class ExperienceSerializer(serializers.HyperlinkedModelSerializer):
user_location = LocationField()
location = LocationField()
class Meta:
model = Experience
fields = ('id', 'company_name', 'company', 'description', 'location',
'title', 'start_date', 'end_date', 'is_current', 'user_location')