Django Rest Framework 中的自定义错误消息

问题描述 投票:0回答:1

我有一个序列化器:

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

我根据另一个问题的

this
答案添加了__init__()方法。但我遇到了一个问题。

如果我发送以下请求:

{
    "email": "[email protected]",
    "password": "password123",
    "company_profile": {
        "name": "Company Test Register1"
    }
}

我得到的回应是:

{
    "field": "email",
    "detail": "user with this email already exists."
}

这是正确的。该响应与 DRF 的默认错误响应不同,因为我使用的是自定义异常处理程序。

但问题是,当我将请求更改为:

{
    "password": "password123",
    "company_profile": {
        "name": "Company Test Register1"
    }
}

我仍然收到相同的错误响应。在服务器重新加载之前,错误响应是相同的。事实上,错误响应应该是:

{
    "field": "email",
    "detail": "Email is required"
}

但是,如果我在请求中发送正确的信息而不重新加载服务器,它将按预期创建公司。

这里还有一些代码以了解更多上下文:

查看:

@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)

自定义异常处理函数:

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

为什么会发生这种情况以及如何解决这个问题?

django exception django-rest-framework custom-errors
1个回答
0
投票

我找到了答案。这是因为

values
函数中的
get_last_value
参数在请求之间没有重置。

我将其更改为以下内容:

def get_last_value(dictionary: dict) -> list[dict]:
    """
    Get the last values from a nested dictionary.
    """
    values = []
    for key, item in dictionary.items():
        if isinstance(item, dict):
            values.extend(get_last_value(item))
        else:
            values.append({key: item})
    return values

现在可以了。花了太长时间才弄清楚这一点。

© www.soinside.com 2019 - 2024. All rights reserved.