making a foreignkey not required - django

I have these 2 models:
# core/models.py
Class Certificate:
user = models.Foreignkey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
related_name='certificates')
name = models.Charfield()
.
.
# users/models.py
Class User(AbstractBaseUser):
.
.
and I want to send the certificates of a user along with its other pieces of information.
this is my serializer:
# users/serializers.py
class CertificationSerializer(serializers.ModelSerializer):
"""Serializer for the certificate object"""
class Meta:
model = Certificate
class UserSerializer(serializers.ModelSerializer):
"""Serializer for the user object"""
# TODO
# videos = serializers.PrimaryKeyRelatedField(
# many=True,
# queryset=Tag.objects.all()
# )
certificates = CertificationSerializer(many=True)
class Meta:
model = get_user_model()
# TODO add certificates and videos
fields = ('id', 'email', 'password', 'first_name', 'last_name',
'phone_number', 'credit', 'points', 'certificates')
extra_kwargs = {'password': {'write_only': True,
'label': 'گذرواژه', 'min_length': 5}}
read_only_fields = ('id', 'certificates',)
def create(self, validated_data):
"""Create a new user and return it"""
return get_user_model().objects.create_user(**validated_data)
def update(self, instance, validated_data):
"""Update a user and return it"""
password = validated_data.pop('password', None)
user = super().update(instance, validated_data)
if password:
user.set_password(password)
user.save()
return user
now when I want to create a new user it tells me that field certificates can not be empty, how can I make the certificates field not required?

Related

Django multiple serializers with relation

I have two tables of users and I am creating a form to store the user information.
Models.py
class MyUser(User):
address = models.CharField(max_length=200)
city = models.CharField(max_length=200)
expire_date = models.DateField()
This creates a table with user_ptr_id to the auth_user table of django.
I created two serializers: one for the User:
class UserSerializer(serializers.ModelSerializer):
first_name = serializers.CharField(min_length=2, required=True)
last_name = serializers.CharField(min_length=2, required=True)
email = serializers.EmailField(min_length=5, required=True)
password = serializers.CharField(min_length=8, write_only=True, required=True)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name', 'password')
def create(self, validated_data):
return UserSerializer(**validated_data)
And MyUser class:
class MyUserSerializer(serializers.ModelSerializer):
address = serializers.CharField(max_length=200)
city = serializers.CharField(max_length=200)
class Meta:
model = MyUser
fields = ('city', 'address')
def create(self, validated_data):
return MyUser(**validated_data)
As I am using Django Rest-Framework-Auth, I craeted a serializer to catch the data, but I don't know how to let the things work together. In the "MyUserSerializer" class, I also perform many validate checks, that I omitted here to keep the code clean.
Code below doesn't work
class UserSignup(serializers.Serializer):
user = UserSerializer()
my_user = MyUserSerializer()
confirm_psw = serializers.CharField(min_length=8, write_only=True, required=True)
def validate(self, data):
if not data["user"].get('password') or not data.get('confirm_psw'):
raise serializers.ValidationError("Please enter a password and confirm it.")
if data["user"].get('password') != data.get('confirm_psw'):
raise serializers.ValidationError("Those passwords don't match.")
return data
def save(self, validated_data):
user_data = self.validated_data["user"]
my_user = self.validated_data["my_user"]
return user_data, my_user
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(user_data)
my_user = validated_data.pop('my_user')
my_user["user_ptr_id"] = user.id
MyUser.objects.create(**ua_user)
return user
How do I merge the two serializers to correctly perform the user registration?
Thanks

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

Invalid username/password error django rest framework custom user serializer

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.

Send Custom Error response from serializer in django rest framework?

I want to send a custom response from serializers create view to front-end of my application. I tried rest framework Response tutorials but it does not work. My code is:
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
"""create a new user"""
firstname = self.initial_data['first_name']
lastname = self.initial_data['last_name']
fullname = str(firstname) +" "+ str(lastname)
email = self.initial_data['username'].lower()
try:
customer = User.create(
name=fullname,
email=email)
except Error as e:
error = {'message': e._message or 'Unknown error'}
return Response(error,status=status.HTTP_400_BAD_REQUEST)
serializers.py
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
"""create a new user"""
firstname = validated_data['first_name']
lastname = validated_data['last_name']
fullname = str(firstname) +" "+ str(lastname)
email = validated_data['username'].lower()
try:
customer = User.objects.create(
name=fullname,
email=email)
return customer
except Exception as e:
error = {'message': ",".join(e.args) if len(e.args) > 0 else 'Unknown Error'}
raise serializers.ValidationError(error)
So you want to change the representation of the User (the shape of the JSON response) from this view. To do this you need to change the .to_representation(self, obj) method in the serializer:
class UserSerializer(serializers.ModelSerializer):
"""Serializer to serialize user model object"""
class Meta:
model = User
fields = ('id', 'username', 'password', 'first_name', 'last_name',)
write_only_fields = ('id', 'password',)
def to_representation(self, obj):
return {
'firstname': obj.first_name,
'lastname': obj.last_name,
'fullname': obj.first_name+' '+obj.last_name,
'email': obj.username.lower()
}
This should mean that whenever a request is made that uses this serializer the user will only ever see those 4 fields in the JSON, e.g.
{
"firstname":"Salman",
"lastname": "Ahmed",
"fullname": "Salman Ahmed",
"email": "salman_ahmed#email.com"
}
More details on this is hidden in their docs here.

DjangoRest serializers: Save Foreignkey relation and serialize

I am new to Django rest and serializers concept
I have this kind of DB structure
User 1->N Address,
User 1->N Phone
User is a django.contrib.auth.models.User. Address and Phone both models contains this field user_id = models.ForeignKey(User, on_delete=models.CASCADE)
In my UserSerializer looks like this
class UserSerializer(serializers.ModelSerializer):
address = AddressSerializer(many=False)
phone = PhoneSerializer(many=False)
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address', 'phone')
def create(self, validated_data):
# Save user.
user = User.objects.create(
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
username=validated_data['username'],
email=validated_data['email'],
)
user.set_password(validated_data['password'])
user.save()
# User's address
address_data = validated_data['address']
address_data['user_id'] = user.id
address = Address.objects.create(**address_data)
# User's phone
phone_data = validated_data['phone']
phone_data['user_id'] = user.id
phone = Phone.objects.create(**phone_data)
return user
And AddressSerializer:
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = (
'user_id', 'address', 'pin_code', 'state', 'city', 'country')
I am able to create User but while doing serializer.data I am getting this error:
AttributeError: Got AttributeError when attempting to get a value for field `address` on serializer `UserSerializer`.
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 'address'.
EDIT:
My create method looks like this
def create(self, validated_data):
password = validated_data.pop('password')
validated_data['username'] = validated_data['email']
user = User.objects.create(**validated_data)
user.set_password(password)
user.save()
return user
Your fields for address and phone should be called address_set and phone_set if you haven't specified related_name explicitly
class UserSerializer(serializers.ModelSerializer):
address_set = AddressSerializer(many=False)
phone_set = PhoneSerializer(many=False)
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address_set', 'phone_set'
)
or
class UserSerializer(serializers.ModelSerializer):
address = AddressSerializer(many=False, source='address_set')
phone = PhoneSerializer(many=False, source='phone_set')
class Meta:
model = User
fields = (
'id', 'username',
'first_name', 'last_name',
'email', 'password',
'address', 'phone'
)