Adding fields to User model - django

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

Related

DRF is not hashing password

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?

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.

Can I build a serializer that validates username and email existence on is_valid call?

guys!
I have this serializer class for the user model
class UserSerializer(serializers.ModelSerializer):
username = serializers.RegexField("^(?=.{6,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$")
full_name = serializers.RegexField("^([a-zA-Z]{2,}\s[a-zA-Z]{1,}'?-?[a-zA-Z]{2,}\s?([a-zA-Z]{1,})?)")
email = serializers.EmailField()
phone = serializers.RegexField("^(\+\d{1,2}\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$")
class Meta:
model = User
fields = ['username', 'full_name', 'email', 'password', 'phone']
This is my serializer class for the moment. I want to validate that the username and email does not exist before saving an user. Can I do it without manually writing the validations? And I also need to validate the regex for the username and the email field for the email
Thank you!
You can validate fields by using validate_{fieldname}:
class UserSerializer(serializers.ModelSerializer):
username = serializers.RegexField("^(?=.{6,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$")
full_name = serializers.RegexField("^([a-zA-Z]{2,}\s[a-zA-Z]{1,}'?-?[a-zA-Z]{2,}\s?([a-zA-Z]{1,})?)")
email = serializers.EmailField()
phone = serializers.RegexField("^(\+\d{1,2}\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$")
class Meta:
model = User
fields = ['username', 'full_name', 'email', 'password', 'phone']
def validate_username(self, value):
if User.objects.filter(username=value).exists():
raise serializers.ValidationError("A user with this username already exists!")
return value
def validate_email(self, value):
if User.objects.filter(email=value).exists():
raise serializers.ValidationError("A user with this email already exists!")
return value
Note that I would personally use these methods to check the input against a regex (with re module) and use CharField for the username field and EmailField for the email.
You can use RegexValidator in your model
class User(models.Model):
username = models.CharField(max_length=128, unique=True, validators=[RegexValidator(regex="^(?=.{6,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(?<![_.])$")])
full_name = models.CharField(max_length=128, validators=[RegexValidator(regex="^([a-zA-Z]{2,}\s[a-zA-Z]{1,}'?-?[a-zA-Z]{2,}\s?([a-zA-Z]{1,})?)")])
email = models.EmailField(max_length=128, unique=True)
phone = models.CharField(max_length=128, unique=True, null=True, validators=[RegexValidator(regex="^(\+\d{1,2}\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$")])
...
Your serializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'full_name', 'email', 'password', 'phone']

Django serializer with a filed that has a OneToOne relationship

In my project I have two 'types' of users: Customers and Businesses. They are an extension of the django base User from django.contrib.auth.models.User.
I have in my models.py:
class Customer(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.CASCADE)
birth_date = models.DateField(blank=True, null=True)
phone = PhoneNumberField(unique=True)
def __str__(self):
return self.user.username
class Business(models.Model):
user = models.OneToOneField(User, related_name='business', on_delete=models.CASCADE)
cf = models.CharField(max_length=16, validators=[ssn_validation])
birth_date = models.DateField(null=True)
city = models.CharField(max_length=50, blank=False)
address = models.CharField(max_length=150, blank=False)
Ok, then I have two different registration, one for Customers and one for Businesses.
A problem is that, to validate the password, sent from a REST API, I need to compare password with password2, create a User (django base), and pass it to my Customer.objects.create, like:
I have in my serializers.py:
class CustomerRegistationSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username',
validators=[UniqueValidator(queryset=User.objects.all())])
email = serializers.CharField(source='user.email',
validators=[UniqueValidator(queryset=User.objects.all())])
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
password = serializers.CharField(source='user.password', write_only=True)
password2 = serializers.CharField(style={'input_style': 'password'}, write_only=True)
birth_date = serializers.CharField(required=False)
class Meta:
model = Customer
fields = ['id', 'username', 'email', 'password', 'password2', 'first_name', 'last_name',
'birth_date', 'phone']
def save(self):
username = self.validated_data['user']['username']
password = self.validated_data['user']['password']
password2 = self.validated_data['password2']
email = self.validated_data['user']['email']
first_name = self.validated_data['user']['first_name']
last_name = self.validated_data['user']['last_name']
phone = self.validated_data['phone']
try:
birth_date = self.validated_data['birth_date']
except KeyError:
birth_date = None
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match!'})
user = User.objects.create(username=username, email=email, first_name=first_name, last_name=last_name)
user.set_password(password)
user.is_active = False
user.save()
customer = Customer.objects.create(user=user,
birth_date=birth_date,
phone=phone)
return customer
That's actually working, but in case of errors can happen that a User is created, but a Customer not.
Is there a cleaner way to make Customers registration, always checking for password == password2?
EDIT: I found a more elegant way to handle this:
#transaction.atomic
def save(self):
password = self.validated_data['user']['password']
password2 = self.validated_data['password2']
user = User.objects.create(**self.validated_data['user'])
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match!'})
user.set_password(password)
user.is_active = False
user.save()
update_last_login(None, user)
del self.validated_data['user']
del self.validated_data['password2']
customer = Customer.objects.create(user=user, **self.validated_data)
return customer
If you want to require that all of the DB transactions you are making during save() method are successful to effectively write it on DB, and not to write anything if there is an error at any point in the process, you are typically asking for atomicity (one of the four ACID capabilities of a Database)
Use this Django decorator, typically made for this:
from django.db import transaction
#transaction.atomic
def save(self):
<...>

AttributeError with non-model Field serialization

I have a model:
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=30, blank=True)
balance = models.DecimalField(max_digits=10, decimal_places=2, default=0)
phone_number = PhoneNumberField(null=True, blank=True)
active = models.BooleanField(default=False)
And I need to serialize creation of User and Model. Idea is to ask User company name while doing registration, so I have:
class CreateUserSerializer(serializers.ModelSerializer):
company_name = serializers.CharField(required=True)
class Meta:
model = User
fields = ('id', 'company_name', 'username', 'email', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
username = validated_data['username']
email = validated_data['email']
password = validated_data['password']
company_name = validated_data['company_name']
user = User.objects.create(username=username, email=email, password=password)
Company.objects.create(user=user, name=company_name)
return user
And I get error:
Got AttributeError when attempting to get a value for field
company_name on serializer CreateUserSerializer. The serializer
field might be named incorrectly and not match any attribute or key on
the User instance. Original exception text was: 'User' object has no
attribute 'company_name'.
Anyway objects Company and User are created as I can see it in my admin panel.
What am I doing wrong and how can I fix it?
You can use source field's argument in this case:
class CreateUserSerializer(serializers.ModelSerializer):
company_name = serializers.CharField(required=True, source='company.name')