ghazi-git / drf-standardized-errors

Standardize your DRF API error responses
https://drf-standardized-errors.readthedocs.io/en/latest/
MIT License
244 stars 13 forks source link

Any hints on how to use the library together with djangorestframework-camel-case? #59

Open xSAVIKx opened 7 months ago

xSAVIKx commented 7 months ago

Thx a lot for working on the library, it really simplifies things a lot and gives us a great boost in documenting everything 👍 🙏

I wanted to wonder if someone has any hints regarding how to make it work together with https://github.com/vbabiy/djangorestframework-camel-case?

The problem I see is that the auto-generated serializers are not camelized. Curious if there's something we can do to make it work together?

I'm using latest versions of all the libraries:

drf-spectacular = "0.27.0"
drf-standardized-errors = {extras = ["openapi"], version = "0.12.6"}
djangorestframework-camel-case = "1.4.2"
ghazi-git commented 7 months ago

If you're looking to customize the output of the exception handler then a custom exception formatter is what you're looking for.

Once you copy that example as is and cause a validation error, you will notice that "field_name" has become "fieldName". That shows that djangorestframework_camel_case runs as usual and highlights the assumption that it makes about the expected output of the API: field names must be keys in a dict. However, drf-standardized-errors intentionally moves away from having field names as keys to make it easier to document errors. So, the 2 do not play well together.

Still, you can always force it in an custom exception formatter class with a workaround like this

from drf_standardized_errors.formatter import ExceptionFormatter
from drf_standardized_errors.types import ErrorResponse, ErrorType
from djangorestframework_camel_case.util import camelize

class MyExceptionFormatter(ExceptionFormatter):
    def format_error_response(self, error_response: ErrorResponse):
        response = super().format_error_response(error_response)
        if error_response.type == ErrorType.VALIDATION_ERROR:
            errors = []
            for e in response["errors"]:
                # I have to pass a dict to camelize to get it to work
                # also, the package settings are not passed to camelize so you might want to do that
                # https://github.com/vbabiy/djangorestframework-camel-case/blob/7d4da4c800252c814d0eae21890f77a91ba04c3f/djangorestframework_camel_case/render.py#L19
                camel_case_value = camelize({e["attr"]: "dummyvalue"})
                error = {**e, "attr": list(camel_case_value)[0]}
                errors.append(error)
            response["errors"] = errors

        return response

For customizing the API schema generated, you might want to start by reading the code of the AutoSchema class provided by drf-standardized-errors to figure out where to make the necessary changes (that might be under AutoSchema._get_serializer_for_validation_error_response)

mikucz commented 7 months ago

@ghazi-git thanks for feedback here and :1st_place_medal: for this package :heart: :muscle:

Working & tested solution for customizing the auto generated API schema:

from drf_standardized_errors.openapi import AutoSchema as DrfAutoSchema
from drf_standardized_errors.openapi_utils import InputDataField

class AutoSchema(DrfAutoSchema):
    """Apply camel case for serializer field names in AutoSchema generated docs"""

    def _get_validation_error_codes_by_field(
        self, data_fields: list[InputDataField]
    ) -> dict[str, set[str]]:
        error_codes_by_field = super()._get_validation_error_codes_by_field(data_fields)
        return {
            to_camelcase(field): error_codes_by_field[field]
            for field in error_codes_by_field
        }
Output in swagger (drf-spectacular) ```python class InputSerializer(serializers.Serializer[Any]): old_password = serializers.CharField() password1 = serializers.CharField() password2 = serializers.CharField() ``` ![image](https://github.com/ghazi-git/drf-standardized-errors/assets/37776903/ea288874-50d5-40e6-b7b0-335f3f0b078c)