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
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.
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
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',..
]
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
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')