I have this user serializer:
class SimpleUser(models.Model):
class meta:
abstract = True
email = models.EmailField(_('email address'), blank=False)
password = models.CharField(_('password'), max_length=128)
first_name = models.EmailField(_('first name'), blank=True)
class UserSerializer(ModelSerializer):
class Meta:
model = SimpleUser
And this is my view:
class UserView(APIView):
def patch(self, request, user_id):
firstname = request.data.get('first_name', '')
email = request.data.get('email', '')
password = request.data.get('password', '')
user = User.objects.get(id=user_id)
serializer = UserSerializer(instance=user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
I send this json request, but only the password and email are updated and first_name not updated.
{
"password":"6524266",
"email":"HH#bb.com",
"first name":"dsfxvxc"
}
I get the status 200 OK and can get the saved object in serializer.save()
What is wrong with my code?
You set firstname as emailfield
first_name = models.EmailField(_('first name'), blank=True)
Changes this to CharField or related fields, something like this,
first_name = models.CharField(max_length=30)
Related
Unable to add a new customer to the database..
I made a class Named customer that has a one-to-one relationship with a class named User that is an AbstractUser
I want to send the data through rest API so that I can create a new customer in the customer table and a new user that is One To One Related to the customer from the same view.
User Model
class User(AbstractUser):
# Add additional fields here
id = None
email = models.EmailField(max_length=254, primary_key=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=100)
is_patient = models.BooleanField(default=False)
is_doctor = models.BooleanField(default=False)
is_homesampler = models.BooleanField(default=False)
is_pathologist = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
first_name = None
last_name = None
username = None
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
def has_perm(self, perm, obj=None):
return self.is_superuser
User Serializer
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
# fields = (['id', 'username', 'email', 'name'])
fields = '__all__'
customer Model
class customer(models.Model):
user = models.OneToOneField(
get_user_model(), on_delete=models.CASCADE, primary_key=True)
real = models.BooleanField(default=False)
def __str__(self):
return self.user.name
Customer Serializer
class CustomerSerializer(serializers.ModelSerializer):
userdata = UserSerializer(read_only=True, source='user')
class Meta:
model = customer
fields = '__all__'
def create(self, validated_data):
user_data = validated_data.pop('user')
user = get_user_model().objects.create(**user_data)
user.is_Patient = True
customer = customer.objects.create(user=user, **validated_data)
return customer
Create Customer View
# Create add customer API
#api_view(['POST'])
def addCustomer(request):
customer_serializer = CustomerSerializer(data=request.data)
if(customer_serializer.is_valid()):
customer_serializer.save()
print(customer_serializer.errors)
return Response({'message': 'okay'})
Body of API Call
{
"email" : "test#test.com",
"password": "Abc"
}
So the question is how can I create a view so that I can create a new user and a customer using just one API Call
Your call body doesn't match CustomerSerializer.
CustomerSerializer fields are "user" and "rest", so you only can pass these two unless you do something like these:
class CustomerSerializer(serializers.ModelSerializer):
userdata = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
class Meta:
model = customer
fields = ["userdata", "email", "password", "id", "real"]
def create(self, validated_data):
email = validated_data.pop("email")
password = validated_data.pop("password")
user = get_user_model().objects.create(**{
"email": email,
"password": password
})
user.is_Patient = True
customer = customer.objects.create(user=user, **validated_data)
return customer
About the create method, it won't function correctly:
Because:
We shouldn't use create to create a new user instead we should use create_user More
I noticed that you removed the username so this method would be useless :)
After user.is_Patient = True, you forgot to save the user
The correct code would be:
class CustomerSerializer(serializers.ModelSerializer):
userdata = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
class Meta:
model = customer
fields = ["userdata", "email", "password", "id", "real"]
def create(self, validated_data):
email = validated_data.pop("email")
password = validated_data.pop("password")
user = get_user_model().objects.create(email=email)
user.set_password(password)
user.is_Patient = True
user.save()
customer = customer.objects.create(user=user, **validated_data)
return customer
NOTE 1:
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
It is the wrong approach to make_password here because whenever you make a change in your user, it would run.
the ideal approach would be using user.set_password("new_pass") whenever you get a new password from the user.
NOTE 2:
When you pass read_only to a serializer, this means would be ignored if you passed it as data to the serializer.
About write_only, it's the opposite of read_only. It would not be returned if you called serializer.data. For example, we only want to write to the password, and we won't want to read it from the serializer, so we made it write_only.
UPDATE: Adding a name
You have to add a write_only field and then pass it to create method
class CustomerSerializer(serializers.ModelSerializer):
userdata = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
name = serializers.CharField(write_only=True)
class Meta:
model = customer
fields = ["userdata", "email", "password", "id", "real", "name"]
def create(self, validated_data):
email = validated_data.pop("email")
password = validated_data.pop("password")
name = validated_data.pop("name")
user = get_user_model().objects.create(email=email, name=name)
user.set_password(password)
user.is_Patient = True
user.save()
customer = customer.objects.create(user=user, **validated_data)
return customer
When I send post request with data in profile model at that time this error show.
Error
{
"user_name": [
"Incorrect type. Expected pk value, received str."
] }
I saw this answer but don't know how to implement SlugRelatedField in serializers(OneToOneField)
models.py:
class CustomUser(AbstractUser):
# username_validator = UnicodeUsernameValidator()
username = models.CharField(
max_length=80,
unique=True,
)
email = models.EmailField(
unique=True,
blank=True,
)
.....
def __str__(self):
return '%s' %(self.username)
class Profile(models.Model):
user_name = models.OneToOneField(to = CustomUser,on_delete=models.CASCADE)
full_name = models.CharField(null=True,max_length=15, blank=True)
public_name = models.CharField(null=True,max_length=15, blank=True)
....
serializers.py:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['user_name','full_name','public_name']
views.py:
class ProfileApiview(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
#Pradip - Have a look at this.
class ProfileSerializer(serializers.ModelSerializer):
user_name = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Profile
fields = ['user_name','full_name','public_name']
def create(self, validated_data):
user = self.context['request'].user
profile = Profile.objects.create(
user_name=user, full_name=validated_data['full_name'].............)
return profile
When I run api request I get a following error:
AttributeError: Got AttributeError when attempting to get a value for field email on serializer UserSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the tuple instance.
Original exception text was: 'tuple' object has no attribute 'email'.
New user gets inserted in database anyway, email field is filleld properly.
View:
class Register(APIView):
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
return Response(serializer.data, status=status.HTTP_201_CREATED)
Serializer:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'name', 'password']
def create(self, validated_data):
user = User.objects.create_user(**validated_data),
return user
Model:
class User(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
username = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=60)
date_of_birth = models.DateField(blank=True, null=True)
bio = models.CharField(default='', max_length=10000)
photo = models.ImageField(max_length=255, null=True, blank=True)
email_verified_at = models.DateTimeField(null=True, blank=True)
email_token_time = models.DateTimeField(null=True, blank=True)
email_token = models.CharField(default='', max_length=64)
password_token_time = models.DateTimeField(null=True, blank=True)
password_token = models.CharField(default='', max_length=64)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
last_seen = models.DateTimeField(null=True, blank=True)
USERNAME_FIELD = 'email'
EMAIL_FIELD = 'email'
REQUIRED_FIELDS = ['username', 'name']
objects = UserManager()
class Meta:
db_table = "User"
def __str__(self):
return self.username
I also have custom user manager, but that is probably irrelevant, and works as user does get inserted to database.
You have typo in this line:
user = User.objects.create_user(**validated_data),
It contains comma , in the last of line. So user become a tuple of user instance, not just user instance. It become (user,).
Should return user instance.
I am trying to build a api for updating first and last name for my user. The api runs successfully but database is not updated which is the expected behavior
I have written the following API and trying to pass the patch request to it.
class UserSelfUpdateView(UpdateAPIView):
serializer_class = UserUpdateSerializer
permission_classes = [UserPermissions, ]
def update(self, request: Request, *args, **kwargs):
instance = User.objects.filter(id=self.request.user.id)
serializer = UserUpdateSerializer(instance, data=request.data,)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({'success': True}, status=status.HTTP_200_OK)
The serializer for the above request is:
class UserUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields: ('id', 'first_name', 'last_name')
The format in which I am trying to pass my request body is:
{
"first_name": "A",
"last_name": "B"
}
and this is how my model is defined:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=False)
last_name = models.CharField(_('last name'), max_length=30, blank=False)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self):
'''
Returns the first_name plus the last_name, with a space in between.
'''
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
'''
Returns the short name for the user.
'''
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
'''
Sends an email to this User.
'''
send_mail(subject, message, from_email, [self.email], **kwargs)
When running code with debug pointer results in the no database update but 200 status.
When running code without debug pointer results in 500 status code and following error message in response
AssertionError at /user/me-edit
("Creating a ModelSerializer without either the 'fields' attribute or the 'exclude' attribute has been deprecated since 3.3.0, and is now disallowed. Add an explicit fields = 'all' to the UserUpdateSerializer serializer.",)
Problem resides in the serializer, and concretely, inside this part of the code:
class Meta:
model = User
fields: ('id', 'first_name', 'last_name')
Fields are also a variable defined using =, so the code should look like:
class Meta:
model = User
fields = ('id', 'first_name', 'last_name')
That will help you on solving that AssertionError.
I'm using UserProfileSerializer to validate fields in patch request:
password = request.data.get('password', '')
if password:
if len(password) < 6:
return Response(status=status.HTTP_400_BAD_REQUEST)
hashed_pass = make_password(password)
serializer = UserProfileSerializer(instance=user,
data={'last_name': request.data.get('last_name', ''),
'password': hashed_pass,
partial=True)
else:
serializer = UserProfileSerializer(instance=user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
and this is my serializer:
class UserProfile(models.Model):
class meta:
abstract = True
password = models.CharField(_('password'), max_length=128, blank=True)
last_name = models.CharField(max_length=30, validators=[MinLengthValidator(3)], blank=True)
class UserProfileSerializer(ModelSerializer):
class Meta:
model = UserProfile
When i'm updating password, the last_name made empty!!
How i prevent it?
This bit of code (which is syntactically invalid (I assume you copy pasted incorrectly)) is to blame
data={'last_name': request.data.get('last_name', ''),
'password': hashed_pass,
partial=True)
If the last name is not in the post data you are setting the last name to blank. You were probably looking for something like:
data={'last_name': request.data.get('last_name', user.last_name),
'password': hashed_pass}
which results in the current last name being preserved.