I'm trying to test a register_customer api endpoint I'm working on on Django, but I'm getting the the user_id as a response instead of the actual user's data.
{
"data": {
"user_id": 15
},
"message": "Thank you for registering"
}
I have a Customer model that has a OneToOne relationship with the CustomUser
class Customer(models.Model):
user_id = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
And here's my serializer:
from rest_framework import serializers
from .models import Customer, CustomUser
import logging
logger = logging.getLogger(__name__)
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
confirm_password = serializers.CharField(write_only=True, required=True)
class Meta:
model = CustomUser
fields = [
"email",
"first_name",
"last_name",
"phone_number",
"password",
"confirm_password",
]
def validate_password(self, value):
# Password must be at least 8 characters long
if len(value) < 8:
raise serializers.ValidationError(
"Password must be at least 8 characters long."
)
# Check for at least one uppercase character
if not any(char.isupper() for char in value):
raise serializers.ValidationError(
"Password must contain at least one uppercase character."
)
# Check for at least one special character
special_characters = "!@#$%^&*()-_=+[]{}|;:'\",.<>/?"
if not any(char in special_characters for char in value):
raise serializers.ValidationError(
"Password must contain at least one special character."
)
# Check for at least one number
if not any(char.isdigit() for char in value):
raise serializers.ValidationError(
"Password must contain at least one number."
)
return value
def validate_email(self, value):
if CustomUser.objects.filter(email=value).exists():
raise serializers.ValidationError("This email is already in use.")
return value
def validate(self, data):
# Check if password and confirm_password match
password = data.get("password")
confirm_password = data.get("confirm_password")
logger.debug({password, confirm_password})
if password != confirm_password:
raise serializers.ValidationError("Passwords do not match.")
return data
def create(self, validated_data):
# Remove 'confirm_password' from the data before creating the user
validated_data.pop("confirm_password", None)
# Retrieve password directly from validated_data
password = validated_data.get("password")
# Ensure that password is not None before validating its length
if password is None:
raise serializers.ValidationError("Password cannot be empty.")
self.validate_password(password)
# Create the user without 'confirm_password'
user = CustomUser.objects.create_user(**validated_data)
# Set the password for the user
user.set_password(password)
user.save()
return user
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ["user_id"]
And here's the view I'm testing:
class RegisterCustomerView(APIView):
@transaction.atomic
def post(self, request, *args, **kwargs):
logger.debug(f"Request_data: {request.data}")
user_data = request.data
# Validate UserSerializer first
user_serializer = UserSerializer(data=user_data)
if user_serializer.is_valid():
# Access validated data after validation
user_data = user_serializer.validated_data
if user_data:
try:
user = user_serializer.save()
customer_data = {"user_id": user.id}
customer_serializer = CustomerSerializer(data=customer_data)
if customer_serializer.is_valid():
customer_serializer.save()
# Automatically send verification email upon successful registration
send_verification_email(user)
return Response(
{
"data": customer_serializer.data,
"message": "Thank you for registering",
},
status=status.HTTP_201_CREATED,
)
else:
raise ValidationError(detail=customer_serializer.errors)
except IntegrityError as e:
logger.error(f"Integrity error: {e}")
return Response(
{"detail": "Integrity error. Please ensure the data is unique."},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")
return Response(
{"detail": "An unexpected error occurred."},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)
else:
raise ValidationError(detail=user_serializer.errors)
I've tried this on the CustomerSerializer
class CustomerSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='user.email')
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
phone_number = serializers.CharField(source='user.phone_number')
class Meta:
model = Customer
fields = ["email", "first_name", "last_name", "phone_number"]
But I didn't get the actual response as expected rather
{
"detail": {
"email": [
"This field is required."
],
"first_name": [
"This field is required."
],
"last_name": [
"This field is required."
],
"phone_number": [
"This field is required."
]
}
}
I'd appreciate any assistance. Thanks
If you want to get CustomUser
data in Customer
model, you can define nested serializer for CustomUser
model and add it to CustomerSerializer
:
class CustomUserNestedSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('pk', 'email', 'first_name', 'last_name', 'phone_number',)
class CustomerSerializer(serializers.ModelSerializer):
user_id = CustomUserNestedSerializer(read_only=True)
class Meta:
model = Customer
fields = ('pk', 'user_id',)
also in your view, you are creating Customer
object by using the serializer. Instead of that, just create customer directly with model and then pass the object to serializer to return it:
customer_obj = Customer.objects.create(user_id=user)
customer_serialized = CustomerSerializer(customer_obj).data
return Response(customer_serialized, status=status.HTTP_20_CREATED)
Your modified serializer for Customer
is ok just modify the source
to user_id
instead of user
as your field name is user_id
in Customer
model and also and read_only=True
to fields.