Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
xSAVIKx opened this issue Jan 12, 2024 · 2 comments

Comments

@xSAVIKx
Copy link

xSAVIKx commented Jan 12, 2024

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
Copy link
Owner

ghazi-git commented Jan 12, 2024

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
Copy link

mikucz commented Feb 6, 2024

@ghazi-git thanks for feedback here and 🥇 for this package ❤️ 💪

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)
class InputSerializer(serializers.Serializer[Any]):
    old_password = serializers.CharField()
    password1 = serializers.CharField()
    password2 = serializers.CharField()

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants