I have a serializer:
<code>class CompanyProfileCreateSerializer(serializers.ModelSerializer):
exclude = ["id", "company"]
class CompanyCreateSerializer(serializers.ModelSerializer):
company_profile = CompanyProfileCreateSerializer(required=True)
password = serializers.CharField(write_only=True)
fields = ["id", "email", "password", "company_profile"]
"password": {"write_only": True, "style": {"input_type": "password"}}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define custom error messages for all fields dynamically
for field_name, field in self.fields.items():
field.error_messages.update({
"required": f"{field_name.replace('_', ' ').capitalize()} is required.",
"null": f"{field_name.replace('_', ' ').capitalize()} cannot be null.",
"invalid": f"Invalid value for {field_name.replace('_', ' ').capitalize()}."
def create(self, validated_data):
company_profile_data = validated_data.pop("company_profile")
company = Company.objects.create(**validated_data, **company_profile_data)
<code>class CompanyProfileCreateSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyProfile
exclude = ["id", "company"]
class CompanyCreateSerializer(serializers.ModelSerializer):
company_profile = CompanyProfileCreateSerializer(required=True)
password = serializers.CharField(write_only=True)
class Meta:
model = Company
fields = ["id", "email", "password", "company_profile"]
extra_kwargs = {
"password": {"write_only": True, "style": {"input_type": "password"}}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define custom error messages for all fields dynamically
for field_name, field in self.fields.items():
field.error_messages.update({
"required": f"{field_name.replace('_', ' ').capitalize()} is required.",
"null": f"{field_name.replace('_', ' ').capitalize()} cannot be null.",
"invalid": f"Invalid value for {field_name.replace('_', ' ').capitalize()}."
})
def create(self, validated_data):
company_profile_data = validated_data.pop("company_profile")
company = Company.objects.create(**validated_data, **company_profile_data)
return company
</code>
class CompanyProfileCreateSerializer(serializers.ModelSerializer):
class Meta:
model = CompanyProfile
exclude = ["id", "company"]
class CompanyCreateSerializer(serializers.ModelSerializer):
company_profile = CompanyProfileCreateSerializer(required=True)
password = serializers.CharField(write_only=True)
class Meta:
model = Company
fields = ["id", "email", "password", "company_profile"]
extra_kwargs = {
"password": {"write_only": True, "style": {"input_type": "password"}}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Define custom error messages for all fields dynamically
for field_name, field in self.fields.items():
field.error_messages.update({
"required": f"{field_name.replace('_', ' ').capitalize()} is required.",
"null": f"{field_name.replace('_', ' ').capitalize()} cannot be null.",
"invalid": f"Invalid value for {field_name.replace('_', ' ').capitalize()}."
})
def create(self, validated_data):
company_profile_data = validated_data.pop("company_profile")
company = Company.objects.create(**validated_data, **company_profile_data)
return company
I added the __init__()
method based on this answer to another question. But I am facing a problem.
If I send the following request:
"email": "[email protected]",
"password": "password123",
"name": "Company Test Register1"
<code>{
"email": "[email protected]",
"password": "password123",
"company_profile": {
"name": "Company Test Register1"
}
}
</code>
{
"email": "[email protected]",
"password": "password123",
"company_profile": {
"name": "Company Test Register1"
}
}
the response I get is:
"detail": "user with this email already exists."
<code>{
"field": "email",
"detail": "user with this email already exists."
}
</code>
{
"field": "email",
"detail": "user with this email already exists."
}
This is correct. The response is different from DRF’s default error response because I am using custom exception handler.
But the problem is that when I change the request to:
"password": "password123",
"name": "Company Test Register1"
<code>{
"password": "password123",
"company_profile": {
"name": "Company Test Register1"
}
}
</code>
{
"password": "password123",
"company_profile": {
"name": "Company Test Register1"
}
}
I still get the same error response. The error response is the same until the server reloads. When in fact the error response should be:
"detail": "Email is required"
<code>{
"field": "email",
"detail": "Email is required"
}
</code>
{
"field": "email",
"detail": "Email is required"
}
But if I send the correct info in the request without the server reloading, it creates the company as expected.
Here are some more pieces of codes for more context:
view:
<code>@extend_schema(tags=["company"])
"""View to create/retrieve/update a company."""
queryset = Company.objects.all()
parser_classes = [JSONParser, MultiPartParser, FormParser]
def get_serializer_class(self):
serializer_action_classes = {
"create": CompanyCreateSerializer,
"retrieve": CompanyRetrieveSerializer,
"update": CompanyUpdateSerializer,
"partial_update": CompanyUpdateSerializer,
if self.action in serializer_action_classes:
return serializer_action_classes[self.action]
raise ValidationError({"detail": "Method Not Allowed."})
def get_permissions(self):
permission_action_classes = {
"retrieve": [IsAuthenticated(), IsOwner()],
"update": [IsAuthenticated(), IsOwner()],
"partial_update": [IsAuthenticated(), IsOwner()],
if self.action in permission_action_classes:
return permission_action_classes[self.action]
def get_serializer_context(self):
context = super().get_serializer_context()
if self.action in ["update", "partial_update"]:
context["instance"] = self.get_object()
@extend_schema(description="Create a new company")
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
@extend_schema(description="Retrieve a single company by User ID")
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@extend_schema(description="Update a single company by User ID")
def update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)
@extend_schema(description="Partially Update a single company by User ID")
def partial_update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
instance = serializer.update(
instance=Company.objects.get(pk=kwargs["id"]),
validated_data=serializer.validated_data,
output_serializer = CompanyRetrieveSerializer(
instance, context={"request": request}
return Response(output_serializer.data)
<code>@extend_schema(tags=["company"])
class CompanyView(
GenericViewSet,
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
):
"""View to create/retrieve/update a company."""
queryset = Company.objects.all()
lookup_field = "id"
parser_classes = [JSONParser, MultiPartParser, FormParser]
def get_serializer_class(self):
serializer_action_classes = {
"create": CompanyCreateSerializer,
"retrieve": CompanyRetrieveSerializer,
"update": CompanyUpdateSerializer,
"partial_update": CompanyUpdateSerializer,
}
if self.action in serializer_action_classes:
return serializer_action_classes[self.action]
else:
raise ValidationError({"detail": "Method Not Allowed."})
def get_permissions(self):
permission_action_classes = {
"create": [AllowAny()],
"retrieve": [IsAuthenticated(), IsOwner()],
"update": [IsAuthenticated(), IsOwner()],
"partial_update": [IsAuthenticated(), IsOwner()],
}
if self.action in permission_action_classes:
return permission_action_classes[self.action]
else:
return [NotAllowed()]
def get_serializer_context(self):
context = super().get_serializer_context()
if self.action in ["update", "partial_update"]:
context["instance"] = self.get_object()
return context
@extend_schema(description="Create a new company")
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
@extend_schema(description="Retrieve a single company by User ID")
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@extend_schema(description="Update a single company by User ID")
def update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)
@extend_schema(description="Partially Update a single company by User ID")
def partial_update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
instance = serializer.update(
instance=Company.objects.get(pk=kwargs["id"]),
validated_data=serializer.validated_data,
)
output_serializer = CompanyRetrieveSerializer(
instance, context={"request": request}
)
return Response(output_serializer.data)
</code>
@extend_schema(tags=["company"])
class CompanyView(
GenericViewSet,
CreateModelMixin,
RetrieveModelMixin,
UpdateModelMixin,
):
"""View to create/retrieve/update a company."""
queryset = Company.objects.all()
lookup_field = "id"
parser_classes = [JSONParser, MultiPartParser, FormParser]
def get_serializer_class(self):
serializer_action_classes = {
"create": CompanyCreateSerializer,
"retrieve": CompanyRetrieveSerializer,
"update": CompanyUpdateSerializer,
"partial_update": CompanyUpdateSerializer,
}
if self.action in serializer_action_classes:
return serializer_action_classes[self.action]
else:
raise ValidationError({"detail": "Method Not Allowed."})
def get_permissions(self):
permission_action_classes = {
"create": [AllowAny()],
"retrieve": [IsAuthenticated(), IsOwner()],
"update": [IsAuthenticated(), IsOwner()],
"partial_update": [IsAuthenticated(), IsOwner()],
}
if self.action in permission_action_classes:
return permission_action_classes[self.action]
else:
return [NotAllowed()]
def get_serializer_context(self):
context = super().get_serializer_context()
if self.action in ["update", "partial_update"]:
context["instance"] = self.get_object()
return context
@extend_schema(description="Create a new company")
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
@extend_schema(description="Retrieve a single company by User ID")
def retrieve(self, request, *args, **kwargs):
return super().retrieve(request, *args, **kwargs)
@extend_schema(description="Update a single company by User ID")
def update(self, request, *args, **kwargs):
return super().update(request, *args, **kwargs)
@extend_schema(description="Partially Update a single company by User ID")
def partial_update(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=True)
serializer.is_valid(raise_exception=True)
instance = serializer.update(
instance=Company.objects.get(pk=kwargs["id"]),
validated_data=serializer.validated_data,
)
output_serializer = CompanyRetrieveSerializer(
instance, context={"request": request}
)
return Response(output_serializer.data)
custom exception handler function:
<code>from rest_framework.views import exception_handler
from rest_framework.response import Response
def get_last_value(dictionary: dict, values = []) -> list[list[str]]:
Get the last values from a nested dictionary.
for key, item in dictionary.items():
if isinstance(item, dict):
get_last_value(item, values)
def custom_exception_handler(exc, context):
Custom exception handler.
"detail": "Error message"
response = exception_handler(exc, context)
if isinstance(response.data, dict):
field = next(iter(response.data))
error_message = get_last_value(response.data)[0][0]
error_response = Response({
}, status=response.status_code)
error_message = str(response.data)
error_response = Response({
"field": "non_field_errors",
}, status=response.status_code)
<code>from rest_framework.views import exception_handler
from rest_framework.response import Response
def get_last_value(dictionary: dict, values = []) -> list[list[str]]:
"""
Get the last values from a nested dictionary.
"""
for key, item in dictionary.items():
if isinstance(item, dict):
get_last_value(item, values)
else:
values.append(item)
return values
def custom_exception_handler(exc, context):
"""
Custom exception handler.
Format of exception:
{
"field": "Field name",
"detail": "Error message"
}
"""
response = exception_handler(exc, context)
if response is not None:
if isinstance(response.data, dict):
field = next(iter(response.data))
error_message = get_last_value(response.data)[0][0]
error_response = Response({
"field": field,
"detail": error_message
}, status=response.status_code)
return error_response
else:
error_message = str(response.data)
error_response = Response({
"field": "non_field_errors",
"detail": error_message
}, status=response.status_code)
return error_response
return response
</code>
from rest_framework.views import exception_handler
from rest_framework.response import Response
def get_last_value(dictionary: dict, values = []) -> list[list[str]]:
"""
Get the last values from a nested dictionary.
"""
for key, item in dictionary.items():
if isinstance(item, dict):
get_last_value(item, values)
else:
values.append(item)
return values
def custom_exception_handler(exc, context):
"""
Custom exception handler.
Format of exception:
{
"field": "Field name",
"detail": "Error message"
}
"""
response = exception_handler(exc, context)
if response is not None:
if isinstance(response.data, dict):
field = next(iter(response.data))
error_message = get_last_value(response.data)[0][0]
error_response = Response({
"field": field,
"detail": error_message
}, status=response.status_code)
return error_response
else:
error_message = str(response.data)
error_response = Response({
"field": "non_field_errors",
"detail": error_message
}, status=response.status_code)
return error_response
return response
Why is this happening and how can I solve this?