Giter VIP home page Giter VIP logo

djangorestframework-camel-case's Introduction

Django REST Framework JSON CamelCase

image

image

Camel case JSON support for Django REST framework.

Installation

At the command line:

$ pip install djangorestframework-camel-case

Add the render and parser to your django settings file.

# ...
REST_FRAMEWORK = {

    'DEFAULT_RENDERER_CLASSES': (
        'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
        'djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer',
        # Any other renders
    ),

    'DEFAULT_PARSER_CLASSES': (
        # If you use MultiPartFormParser or FormParser, we also have a camel case version
        'djangorestframework_camel_case.parser.CamelCaseFormParser',
        'djangorestframework_camel_case.parser.CamelCaseMultiPartParser',
        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
        # Any other parsers
    ),
}
# ...

Add query param middleware to django settings file.

# ...
MIDDLEWARE = [
    # Any other middleware
    'djangorestframework_camel_case.middleware.CamelCaseMiddleWare',
]
# ...

Swapping Renderer

By default the package uses rest_framework.renderers.JSONRenderer. If you want to use another renderer, the two possible are:

drf_orjson_renderer.renderers.ORJSONRenderer or drf_ujson.renderers.UJSONRenderer or rest_framework.renderers.UnicodeJSONRenderer for DRF < 3.0,specify it in your django settings file. settings file.

# ...
JSON_CAMEL_CASE = {
    'RENDERER_CLASS': 'drf_orjson_renderer.renderers.ORJSONRenderer'
}
# ...

Underscoreize Options

No Underscore Before Number

As raised in this comment there are two conventions of snake case.

# Case 1 (Package default)
v2Counter -> v_2_counter
fooBar2 -> foo_bar_2

# Case 2
v2Counter -> v2_counter
fooBar2 -> foo_bar2

By default, the package uses the first case. To use the second case, specify it in your django settings file.

REST_FRAMEWORK = {
    # ...
    'JSON_UNDERSCOREIZE': {
        'no_underscore_before_number': True,
    },
    # ...
}

Alternatively, you can change this behavior on a class level by setting `json_underscoreize`:

from djangorestframework_camel_case.parser import CamelCaseJSONParser
from rest_framework.generics import CreateAPIView

class NoUnderscoreBeforeNumberCamelCaseJSONParser(CamelCaseJSONParser):
    json_underscoreize = {'no_underscore_before_number': True}

class MyView(CreateAPIView):
    queryset = MyModel.objects.all()
    serializer_class = MySerializer
    parser_classes = (NoUnderscoreBeforeNumberCamelCaseJSONParser,)

Ignore Fields

You can also specify fields which should not have their data changed. The specified field(s) would still have their name change, but there would be no recursion. For example:

data = {"my_key": {"do_not_change": 1}}

Would become:

{"myKey": {"doNotChange": 1}}

However, if you set in your settings:

REST_FRAMEWORK = {
    # ...
    "JSON_UNDERSCOREIZE": {
        # ...
        "ignore_fields": ("my_key",),
        # ...
    },
    # ...
}

The my_key field would not have its data changed:

{"myKey": {"do_not_change": 1}}

Ignore Keys

You can also specify keys which should not be renamed. The specified field(s) would still change (even recursively). For example:

data = {"unchanging_key": {"change_me": 1}}

Would become:

{"unchangingKey": {"changeMe": 1}}

However, if you set in your settings:

REST_FRAMEWORK = {
    # ...
    "JSON_UNDERSCOREIZE": {
        # ...
        "ignore_keys": ("unchanging_key",),
        # ...
    },
    # ...
}

The unchanging_key field would not be renamed:

{"unchanging_key": {"changeMe": 1}}

ignore_keys and ignore_fields can be applied to the same key if required.

Running Tests

To run the current test suite, execute the following from the root of he project:

$ python -m unittest discover

License

  • Free software: BSD license

djangorestframework-camel-case's People

Contributors

adam-rescale avatar akiyoko avatar alaminopu avatar andriykohut avatar blenq21 avatar dbrgn avatar dependabot[bot] avatar dgilge avatar dickenwong avatar gopackgo90 avatar gradam avatar ilianiliev avatar insung151 avatar jacobg avatar jairhenrique avatar jakob-o avatar jerr0328 avatar jhominal avatar li-junyu avatar lk-geimfari avatar magul avatar ondrowan avatar romanosipenko avatar timeu avatar vbabiy avatar yatoff avatar ypcrumble avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

djangorestframework-camel-case's Issues

Camelizing schema generator

Right now if you use this package and DRF's OpenAPI schema generator, the fields don't end up camelized. I have kludged together a quick fix for this:

class CamelizingAutoSchema(AutoSchema):
    def _map_serializer(self, serializer):
        result = super()._map_serializer(serializer)
        camelized_properties = {
            camelize_str(field_name): schema
            for field_name, schema in result["properties"].items()
        }
        new_result = {"type": "object", "properties": camelized_properties}
        if "required" in result:
            new_result["required"] = list(map(camelize_str, result["required"]))

        return new_result

It works for me and it seems like a good fit for this project, but I thought I'd check in first before making a PR because I'm not sure if it's an amazing idea. It's very useful and works, but overriding a private API (_map_serializer()) doesn't seem great, especially since DRF's schema generation is quite new and a bit of a moving target at the moment.

But if you'd like I can add some tests for this and submit a PR, but it might need some maintenance.

CamelCaseJSONParser does not care about the field whose name ends with more than two digit

Summary

CamelCaseJSONParser does not care about the field whose name ends with more than one digit like this:

  • 'key10' is coverted to 'key_1_0'
  • 'anotherKey10' is coverted to 'another_key_1_0'

I hope the following will occur:

  • 'key10' would be 'key_10'
  • 'anotherKey10' would be 'another_key_10'

Details

My serializer has some fields as follows:

api/serializers.py

from rest_framework import serializers

class FooSerializer(serializers.Serializer):
    key_1 = serializers.IntegerField()
    key_10 = serializers.IntegerField()
    another_key_1 = serializers.IntegerField()
    another_key_10 = serializers.IntegerField()

When I posted the following JSON data to the API using the above serializer,

{
  "key1": 1,
  "key10": 2,
  "anotherKey1": 3,
  "anotherKey10": 4
}

I got the following error response with the status code 400:

{
  "key10":[
    "This field is required."
  ],
  "anotherKey10":[
    "This field is required."
  ]
}

Other prerequisites are as follows:

api/views.py

from rest_framework import status, views
from .serializers import FooSerializer

class FooAPIView(views.APIView):

    def post(self, request, *args, **kwargs):
        serializer = FooSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data, status.HTTP_200_OK)

mysite/urls.py

from django.urls import path
from api import views as api_view

urlpatterns = [
    path('api/foo/', api_view.FooAPIView.as_view()),
]

mysite/settings.py

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.AllowAny',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
        'rest_framework.renderers.JSONRenderer',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
        'rest_framework.parsers.JSONParser',
    ),
}
>pip list
Package                        Version
------------------------------ -------
Django                         2.2.3
djangorestframework            3.9.4
djangorestframework-camel-case 1.0.3

Cause

CamelCaseJSONParser does not care about the pattern which ends with more than one digit.
The points that should be revised are as follows:

diff --git a/djangorestframework_camel_case/util.py b/djangorestframework_camel_case/util.py
index d8a62d1..5780ecf 100644
--- a/djangorestframework_camel_case/util.py
+++ b/djangorestframework_camel_case/util.py
@@ -42,7 +42,7 @@ def get_underscoreize_re(options):
     if options.get("no_underscore_before_number"):
         pattern = r"([a-z]|[0-9]+[a-z]?|[A-Z]?)([A-Z])"
     else:
-        pattern = r"([a-z]|[0-9]+[a-z]?|[A-Z]?)([A-Z0-9])"
+        pattern = r"([a-z]|[0-9]+[a-z]?|[A-Z]?)([A-Z]|[0-9]+)"
     return re.compile(pattern)


diff --git a/tests.py b/tests.py
index 8fa7b0b..9c9acc6 100644
--- a/tests.py
+++ b/tests.py
@@ -58,6 +58,7 @@ class CamelToUnderscoreTestCase(TestCase):
             "bOnlyOneLetter": 5,
             "onlyCLetter": 6,
             "mix123123aAndLetters": 7,
+            "underscoreBefore123": 8,
         }
         output = {
             "two_word": 1,
@@ -67,6 +68,7 @@ class CamelToUnderscoreTestCase(TestCase):
             "b_only_one_letter": 5,
             "only_c_letter": 6,
             "mix_123123a_and_letters": 7,
+            "underscore_before_123": 8,
         }
         self.assertEqual(underscoreize(data), output)

Setting to preserve _leadingUnderscores

Hi folks, in my API I have some keys with _leading_underscores, for example:

{
    '_str': 'String',
    '_timestamp': 1234567890,
    'firstValue': '...',
    'secondValue: '...'
}

I want to preserve these leading _'s, but camelize the other fields.

I am thinking about a PR that adds a setting PRESERVE_LEADING_UNDERSCORE, which would allow camelize_re in utils.py to be [a-z0-9]_[a-z0-9] instead of [a-z0-9]?_[a-z0-9] - note the changed '?'.

This would probably involve changing camelize_re from being a constant to being generated by a utils.py function, perhaps get_camelize_re().

Thoughts?

Does not work with tuples

Using it with a tuple data raise the following exception 'tuple' object does not support item assignment.
Perhaps it should cast tuples to list before that, I'll try to create a patch.

Not work with the serializer field contains number

Environment

  • Django==2.0

Abstract

We define some serializer field contains number like password1, the serializer can't detect field and raise required error.

Example

We define the endpoint to change password. The Serializer has two fields, i.e.

  • new_password1
  • new_password2

, then post to it with the body as

{
  "newPassword1": "hogehoge",
  "newPassword2": "hogehoge"
}

. We have required field error like

capture 2019-02-05 16 33 20

Downgrade djangorestframework-camel-case to 0.2.0, it works.

Renderer doesn't work with generators

Hello,

first thank you for this module. :) Second, I found out it has problem if data in renderer are in form of a generator. Problem is in function camelize. Instead of checking if instance is list or tuple it should check if instance is iterable. Could you please fix it?

Does not work with DRF 3.0

This does not appear to work with DRF 3.0 on POST or PUTs. It renders fine, but does not work when camel cased fields are passed to create or update items.

Field lists in query parameters such as DRF's ordering filter

When using rest_framework.filters.OrderingFilter in a view, one can add a list of comma-separated field names to an "order" query parameter (see the docs). However, those lists are currently not affected by this package, making it incompatible with it out-of-the-box. I solved this, by extending the get_ordering method of the OrderingFilter class:

from rest_framework.filters import OrderingFilter
from djangorestframework_camel_case.util import camel_to_underscore
from djangorestframework_camel_case.settings import api_settings


class CamelCaseOrderingFilter(OrderingFilter):

    def get_ordering(self, request, queryset, view):

        params = request.query_params.get(self.ordering_param)
        if params:
            fields = [camel_to_underscore(field.strip(), **api_settings.JSON_UNDERSCOREIZE,)
                      for field in params.split(',')]
            ordering = self.remove_invalid_fields(queryset, fields, view, request)
            if ordering:
                return ordering

        return self.get_default_ordering(view)

Now there are other packages using field lists in query parameters. I am currently trying to add drf-flex-fields to my app and I haven't found a good way to customize it to use "underscoreize" the fields and expand lists in this case.

Has anyone else faced this problem and has a solution? Or is it maybe in the scope of this project to provide a solution for field lists in query parameters?

Doesn't work when used with rest_framework_gis serializers

Hello all,

I've been using this renderer and parser with DRF 3.0 and while it has been working great, I had to ditch it now that I also need to use serializers from rest_framework_gis, particularly the GeoModelSerializer.

Here is a JSON example I'm POSTing:

{
  "id": "streetlight:guadalajara:4567",
  "type": "Streetlight",
  "location": {
    "type": "Point",
    "coordinates": [  -3.164485591715449, 40.62785133667262 ]
  }
}

and here is my serializer:

class StreetlightSerializer(gis_serializers.GeoModelSerializer):

    class Meta:
        model = Streetlight
        fields = ('id', 'type', 'created', 'location', 'ref_streetlight_model',
                  'url', 'ref_streetlight_group', 'ref_streetlight_control_cabinet')

        geo_field = 'location'

and model:

class Streetlight(models.Model):
    ...
    location = PointField(
        srid=4326, blank=True, null=True
    )

When using this lib as rest_framework's default render and parser, I get the following traceback https://pastebin.com/nxNwhE7T

Is there any easy clean to get past this, without manually invoking the parser / renderer at the right time (before the serializer) ?

Thanks!

EDIT
Actually, this happens when my serializer is also set to serializers.ModelSerializer - so I guess I'll have to take care of this location field (GeoJSON) manually in my serializer, right?

Releases

Please start making Change Log / using GitHub Releases, so it's possible to know what was changed (like #50)

Ignore fields does not work as expected

settings.py

"JSON_UNDERSCOREIZE": {
    # ...
    "ignore_fields": ("order_update", ),
    # ...
}

Still got output response:

{
"orderUpdate": {
"items": [
{
"parent": null,
"type": "tax",
"description": "Sales taxes",
"amount": 350,
"currency": "usd"
}
],
"shipping_methods": [
{
"id": "free_shipping",
"description": "Free 7-day shipping",
"amount": 0,
"currency": "usd",
"delivery_estimate": {
"type": "exact",
"date": "2020-08-11"
},
"tax_items": []
}
]
}
}

Need escape hatch

It would be useful to have a way to let an object be rendered without transformation.

Suggestion: add a marker class used like return Response({'valid_values': nocamel(some_choices)}) and change the camelize function to not change these objects. The result here would be {"validValues": [["thing_one", "Thing one"], ["thing_two", "Thing two"]]}.

rendering a dict response only returns a list of keys since #34

First of all, thanks for building this package. It's very convenient!

I recently updated to the latest release and noticed that rendering dict responses from DRF is broken. It looks like the iterator support added in #34 clobbers the original dict handling. The dict response is camelized correctly, but then the is_iterable(data) conditional passes and immediately the dict is converted into a list of just the keys and that is what is returned.

I'm wondering if the intent was to make that an elif conditional instead?

'djangorestframework_camel_case.parser.CamelCaseJSONParser' does not work with DRF 3.10.0

Details

My DRF project is under the following conditions.

  1. packages
>pip list
Package                        Version
------------------------------ -----------
Django                         2.2.3
...
djangorestframework            3.10.0
djangorestframework-camel-case 1.0.3
...
six                            1.12.0
  1. mysite/settings.py
REST_FRAMEWORK = {
    ...
    'DEFAULT_PARSER_CLASSES': (
        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
        'rest_framework.parsers.JSONParser',
    ),
    ...
}

This raises the following error while starting up runserver.

...
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\generics.py", line 9, in <module>
    from rest_framework import mixins, views
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\views.py", line 104, in <module>
    class APIView(View):
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\views.py", line 108, in APIView
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\settings.py", line 220, in __getattr__
    val = perform_import(val, attr)
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\settings.py", line 168, in perform_import
    return [import_from_string(item, setting_name) for item in val]
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\settings.py", line 168, in <listcomp>
    return [import_from_string(item, setting_name) for item in val]
  File "C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\settings.py", line 180, in import_from_string
    raise ImportError(msg)
ImportError: Could not import 'djangorestframework_camel_case.parser.CamelCaseJSONParser' for API setting 'DEFAULT_PARSER_CLASSES'. ImportError: cannot import name 'six' from 'rest_framework.parsers' (C:\Users\akiyoko\PycharmProjects\myproject\venv\lib\site-packages\rest_framework\parsers.py).

Cause

This error seems caused by the compatibility between [email protected] and [email protected].

DRF 3.10 release dropped support for Python 2 and rest_framework/[email protected] removed the import of six, but djangorestframework_camel_case/parser.py needs rest_framework.parsers.six.

This is the first part of djangorestframework_camel_case/parser.py@master.

...
import six
...
from rest_framework.parsers import six, FormParser

Now, I tried to fix djangorestframework_camel_case/parser.py as follows, it works perfectly.

...
import six
...
from rest_framework.parsers import FormParser

Related Links

Does not work with multipart upload

I am using restframework 3.11.0, camel case 1.1.2 and unfortunately it doesn't seem to work for Multipart files when I add MultipartParser like below

class JobPostViewSet(viewsets.ModelViewSet):
    parser_classes = (parsers.MultiPartParser,)

I have to send the data in snake case for my api to work when I am using MultipartParser

tag new version

can you generate a new version with Underscoreize feature? because the version installed by pip does not have this feature.

thank you!

Does this work using django.http.JsonResponse?

Hi there! Very nice tool, thanks!

Would this this library also work with Django's JsonResponse?

According to Django's doc:
https://docs.djangoproject.com/en/2.0/ref/request-response/#jsonresponse-objects
an encoder can be set.

response = JsonResponse(data, encoder=MyJSONEncoder)

For example, in views.py:

from django.http import JsonResponse
from .models import User
from .serializers import UserSerializer

def user_view(request):

    user = User.objects.get(pk=1)

    return JsonResponse(UserSerializer(user).data, encoder=CamelCaseJSONEncoder)

That'd be pretty elegant... ;)

Django Rest Witchcraft support

Hi everyone,

Thanks for maintaining such a great resource.

Unfortunately, I am having trouble getting this package to work with Django Rest Witchcraft which handles serializing models against SQLAlchemy.

Problem

I have added the default renderer and parser classes to my settings.py as follows (following the instructions in the README.md:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': os.environ.get('PAGINATION_SIZE'),

    'DEFAULT_RENDERER_CLASSES': (
        'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
        'djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer',
        # Continue renders
    ),

    'DEFAULT_PARSER_CLASSES': (
        'djangorestframework_camel_case.parser.CamelCaseFormParser',
        'djangorestframework_camel_case.parser.CamelCaseMultiPartParser',
        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
        # continue parsers
    ),
}

I am also using Yet another Swagger generator to generate docs for the API.

Both the docs, swagger and ReDoc, and the corresponding json output still show underscores in my request.

Question

What areas should I look in for as to why the parser/renderer is not parsing the camelCase output as expected?

Version 1.0.3 in PyPI differs from version 1.0.3 on GitHub

When you install version 1.0.3 from PyPI by pip install djangorestframework-camel-case you get completely different code than on GitHub.

Few examples of differencies:
render module is missing class CamelCaseBrowsableAPIRenderer
parser module contains: from rest_framework.parsers import six, FormParser

Initial problem

I having a problem when I get JSON data like:
>>> underscoreize({'HelloWord': 'HelloWorld'})
{'_hello_word': 'HelloWorld'}

Am i using it a wrong way?
I got data from an API and this problem confused me.
It seems no other issue refer to this case.
Sorry about my bad English, and thanks for your work.

error when value is a translation

When the value to render is a translation (ugettext_lazy) the string is converted to array of strings.

I think the problem is the is_iterable method in util.py, because return true when the value is a translation.

Example of response:

{
    "detail": [
        "S",
        "e",
        " ",
        "h",
        "a",
        " ",
        "e",
        "n",
        "v",
        "i",
        "a",
        "d",
        "o",
        " ",
        "u",
        "n",
        " ",
        "c",
        "o",
        "r",
        "r",
        "e",
        "o",
        " ",
        "e",
        "l",
        "e",
        "c",
        "t",
        "r",
        "ó",
        "n",
        "i",
        "c",
        "o",
        " ",
        "p",
        "a",
        "r",
        "a",
        " ",
        "r",
        "e",
        "s",
        "t",
        "a",
        "b",
        "l",
        "e",
        "c",
        "e",
        "r",
        " ",
        "l",
        "a",
        " ",
        "c",
        "o",
        "n",
        "t",
        "r",
        "a",
        "s",
        "e",
        "ñ",
        "a",
        "."
    ]
}

variable names like `accepted_TOS` do not work

For sure, a variable named accepted_TOS isn't ideal. At the same time, it's a valid python variable name, yet this library wouldn't work with it.

When trying to consume my api, I tried the following:

acceptedTOS
acceptedTos
accepted_TOS
acceptedtos
acceptedT
acceptedt

All invalid. As soon as I disabled this package and tried it with the python style variable names it worked.
So something is being bungled within this package, given the way it mishandles that variable.

See here for reference. I ultimately went and changed the variable name to something more pythonic, but in my opinion the old name ought to work as it is a valid python variable or model field name.

Use renderer in specific APIView

Hi! Is there a way to apply the renderer class to a specific APIWiev instance? I have already so many endpoints created that do not return camelCase keys so putting the renderer method as a default render will break existing things. So I tried setting it up like this but that did not work for me:

from djangorestframework_camel_case.render import CamelCaseJSONRenderer


class ActiveSubscriptionsAPIView(CamelCaseJSONRenderer, APIView):
    stuff

Thanks in advance!

integer key/values in response can't be handled

throws TypeError: expected string or bytes-like object.
I'm guessing we should gracefully handle and ignore keys that are not string (such as integer), rather than force them to be string.

Bad underscorization; util.camel_to_underscore('anotherKey1') returns 'another_key1'

I found weird underscorization.

>>> from djangorestframework_camel_case.util import camel_to_underscore
>>> camel_to_underscore('anotherKey10')
'another_key_10'

That's OK :)

>>> camel_to_underscore('anotherKey1')
'another_key1'

That's weird. I hope that it should return 'another_key_1'.

My requirements are as follows:

Django==2.2.6
djangorestframework==3.10.3
djangorestframework-camel-case==1.1.2

Could not import 'rest_framework.renderers.UnicodeJSONRenderer'

I configured my django according to guide. The error occurs:

ImportError: Could not import 'rest_framework.renderers.UnicodeJSONRenderer' for API setting 'RENDERER_CLASS'. AttributeError: module 'rest_framework.renderers' has no attribute 'UnicodeJSONRenderer'.

Changing UnicodeJSONRenderer to JSONRenderer another error occurs:

ImportError: Could not import 'djangorestframework_camel_case.parser.CamelCaseFormParser' for API setting 'DEFAULT_PARSER_CLASSES'. ModuleNotFoundError: No module named 'six'.

Field sort order

Hey, I like your package, unfortunately it reorders the field names in alphabetical order opposed to declaration order. Though computer don't mind I would prefer having the fields in declaration order.

Edit: Created a pull request.

camel_to_underscore doesn't respect spaces

I'm definitely using the camel_to_underscore function in an unexpected way (I'm transforming values as well as keys in some cases), but I think you'll agree this is bad behavior:

from djangorestframework_camel_case.util import camel_to_underscore
camel_to_underscore("one Two")
>>> 'one _Two'

The first_cap_re regexp is a little naive. I'm not sure what the exact fix is, but there you go.

Underscoreize GET params

Is there any option to underscoreize GET params with this package? If not what is the best option to do that, add custom middleware?

Ability to selectively camelize/underscorize fields

Some time ago I looked at using this project for converting JSON responses from underscore case to camelcase but the problem I ran into is that it doesn't distinguish between keys that are values and keys that are field names

For example:

import djangorestframework_camel_case.util as util
data = {
    'first_name': 'Mary',
    'last_name': 'Smith',
    'servers': {
        'server_west.mycompany.com': '...',
        'server_south.mycompany.com': '...',
    },
}
print(json.dumps(util.camelize(data), indent=4))

Output is:

{
    "firstName": "Mary",
    "lastName": "Smith",
    "servers": {
        "serverWest.mycompany.com": "...",
        "serverSouth.mycompany.com": "..."
    }
}

There's no way of indicating that the keys in the servers object shouldn't be modified because it's an associative array where the keys are user-supplied data.

I created an alternate implementation that adds an ignore parameter, so eg you could do the following:

util.camelize(data, ignore=['servers'])

Anything in the tree that matches the ignore specifier will be ignored (it will still recurse into that node but won't change the case on the key, and also handles arrays; ignore=['servers', 'servers.*.user_accounts'] would allow you to correctly transform something like:

{
    "first_name": "Mary",
    "last_name": "Smith",
    "servers": {
        "server_west.mycompany.com": {
            'user_accounts': {
                'mary_smith': {
                    'home_dir': '/home/mary_smith',
                },
            },
        },
    },
}

into

{
    "firstName": "Mary",
    "lastName": "Smith",
    "servers": {
        "server_west.mycompany.com": {
            'userAccounts': {
                'mary_smith': {
                    'homeDir': '/home/mary_smith',
                },
            },
        },
    },
}

If I cleaned up the code is it something that you'd be interesting in accepting a PR for or outside the scope of this project?

underscore_to_camel is not working with str

I am using the library already so I would like to use the underscore_to_camel too.
but its not working with strings.
Getting *** AttributeError: 'str' object has no attribute 'group' error

DRF 3.10 compatibility

DRF 3.10 dropped support for Python 2. As such, it is no longer to import six from restframework.compat

Does this work for nested JSON?

Looks like it will work for the top level keys in a json data structure, but does it work for nested data like this? Does not seems to work for nestedKey in the scenario below.

{
  someKey1: "value 1"
  someKey2: "value 2"
  someKey3: [{"nestedKey":"nested value"}]
}

cannot import name 'six' from 'rest_framework.parsers'

When I upgrade the version of django rest framework to 3.10.0, the project cannot start up and raise this exception.

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/settings.py", line 177, in import_from_string
    return import_string(val)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/utils/module_loading.py", line 17, in import_string
    module = import_module(module_path)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/djangorestframework_camel_case/parser.py", line 12, in <module>
    from rest_framework.parsers import six, FormParser
ImportError: cannot import name 'six' from 'rest_framework.parsers' (/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/parsers.py)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 917, in _bootstrap_inner
    self.run()
  File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 865, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/utils/autoreload.py", line 54, in wrapper
    fn(*args, **kwargs)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run
    self.check(display_num_errors=True)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/management/base.py", line 390, in check
    include_deployment_checks=include_deployment_checks,
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/management/base.py", line 377, in _run_checks
    return checks.run_checks(**kwargs)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/checks/registry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/checks/urls.py", line 13, in check_url_config
    return check_resolver(resolver)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/core/checks/urls.py", line 23, in check_resolver
    return check_method()
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/urls/resolvers.py", line 398, in check
    for pattern in self.url_patterns:
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/utils/functional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/urls/resolvers.py", line 579, in url_patterns
    patterns = getattr(self.urlconf_module, "urlpatterns", self.urlconf_module)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/utils/functional.py", line 80, in __get__
    res = instance.__dict__[self.name] = self.func(instance)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/urls/resolvers.py", line 572, in urlconf_module
    return import_module(self.urlconf_name)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/xiaohe/Projects/parasite-python/para_latipay/urls.py", line 22, in <module>
    path('web/', include('para_latipay.para_web.urls')),
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/django/urls/conf.py", line 34, in include
    urlconf_module = import_module(urlconf_module)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/xiaohe/Projects/parasite-python/para_latipay/para_web/urls.py", line 3, in <module>
    from para_latipay.para_web.report_utils.ato_report_view import get_ato_report
  File "/Users/xiaohe/Projects/parasite-python/para_latipay/para_web/report_utils/ato_report_view.py", line 7, in <module>
    from rest_framework.decorators import api_view, permission_classes
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/decorators.py", line 13, in <module>
    from rest_framework.views import APIView
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/views.py", line 104, in <module>
    class APIView(View):
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/views.py", line 108, in APIView
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/settings.py", line 220, in __getattr__
    val = perform_import(val, attr)
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/settings.py", line 168, in perform_import
    return [import_from_string(item, setting_name) for item in val]
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/settings.py", line 168, in <listcomp>
    return [import_from_string(item, setting_name) for item in val]
  File "/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/settings.py", line 180, in import_from_string
    raise ImportError(msg)
ImportError: Could not import 'djangorestframework_camel_case.parser.CamelCaseJSONParser' for API setting 'DEFAULT_PARSER_CLASSES'. ImportError: cannot import name 'six' from 'rest_framework.parsers' (/Users/xiaohe/Projects/parasite-python/venv/lib/python3.7/site-packages/rest_framework/parsers.py).

When saving ForeignKey, parser defaults field to null

Hello all,

This was working fine until I started saving ForeignKeys. No matter the JSON property type (string / integer), it always stores null.

A model:

class Streetlight(models.Model):
    refStreetlightModel = models.ForeignKey(
        'StreetlightModel', null=True, blank=True, on_delete=models.SET_NULL)

A serializer:

class StreetlightSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Streetlight
        fields = ('id', 'type', 'created', 'refStreetlightModel', 'url')

If I do remove

 'DEFAULT_RENDERER_CLASSES': (
        'djangorestframework_camel_case.render.CamelCaseJSONRenderer',
        # Any other renders
    ),
    'DEFAULT_PARSER_CLASSES': (
        'djangorestframework_camel_case.parser.CamelCaseJSONParser',
        # Any other parsers
    ),

from my settings, It all works as expected. When I POST

{
  ...
  "refStreetlightModel": "/streetlightmodels/17/",
  ...
}

I do get the full url back when issuing a GET on this saved value. However, when using this parser and renderer, refStreetlightModel field is always stored as null.

Am I doing something wrong? Any help would be much appreciated!

Cheers

Can't assign items to tuple

Hello and thanks for making this package! I have a field that exists as a tuple before being serialized to a list. With drf-camel-case, there is an exception coming from the camelize function:

if isinstance(data, (list, tuple)):
    for i in range(len(data)):
        data[i] = camelize(data[i])

Tuples don’t support item assignment.

Suggested fix:

if isinstance(data, tuple):
    data = list(data)
if isinstance(data, list):
    for i in range(len(data)):
        data[i] = camelize(data[i])

`ValidationError` field names are not camelized

When I raise rest_framework.serializers.ValidationError({'some_field_name': 'some message'}) the resulting API response is {'some_field_name': [ErrorDetail(string='some message', code='invalid')]}. The expected response would be {'someFieldName': [ErrorDetail(string='some message', code='invalid')]}.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.