CLI tool
vbabiy/djangorestframework-camel-case avatar
vbabiy/djangorestframework-camel-case

djangorestframework-camel-case renames JSON keys at the renderer boundary

Camel case JSON support for Django REST framework.

674 stars124 forksPythonNOASSERTION

At a glance

What is it?
djangorestframework-camel-case is a small Django REST framework add-on that turns snake_case keys into camelCase on the way out and back on the way in, through a custom renderer, parser and query parameter middleware. It ships one opinionated naming rule for digits, ignore lists, and a class level override, and it delegates the actual conversion to StringManipulation.
Who is it for?
Adopt djangorestframework-camel-case if your frontend contract requires camelCase while your Django models and serializers stay in snake_case, and if you would rather put the conversion at the DRF boundary than rewrite every serializer. Do not adopt it if your API is consumed only by Python or by clients that already handle snake_case, since the package adds a layer to every request and response for no gain there.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 95 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The mismatch this package exists to hide

Django and Django REST framework work in snake_case, because that is Python's convention, and the ORM field names, the serializer field names and the model attribute names all follow it. A modern frontend codebase frequently works in camelCase, because that is what JavaScript and TypeScript code tends to produce and because the JSON contract is often written by a frontend team.

That mismatch produces a familiar pile of small work. Either the frontend maps every field by hand, or the backend exposes a second representation of every resource, or somebody writes a parallel serializer for the frontend. djangorestframework-camel-case takes the third option and generalises it, converting keys at the boundary so the models and serializers keep snake_case while the wire format is camelCase.

The project is deliberately small. The package directory is djangorestframework_camel_case, the repository top level holds a single tests.py alongside setup.py, a Makefile, tox.ini, requirements.txt, HISTORY.rst and AUTHORS.rst, and the one line description on the package index is camel case JSON support for Django REST framework.

The conversion itself is not implemented here. The requirements list djangorestframework of at least 3.13.0 and django of at least 3.2, and when the README explains the digit convention it links to a comment on an issue in the StringManipulation project. So this package is an adapter that plugs a naming library into the DRF renderer and parser classes, which is why the naming rules it offers are exactly the rules that library exposes.

Three settings entries: renderer, parser, middleware

Installation is a pip install, and then three separate things have to be wired into your Django settings.

bash
    $ pip install djangorestframework-camel-case

The first is the renderer and parser classes. The package offers a camel case JSON renderer, a camel case browsable API renderer, and three parsers covering form data, multipart form data and JSON:

python
        '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
        ),

The second is the query parameter middleware, which is a separate entry in the middleware list:

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

That third piece is the one people forget. If you swap only the renderer and the parser, query string parameters stay snake_case while the JSON body becomes camelCase, so a request such as /orders/?created_at=2026-01-01 works while the body keys do not match the form parameters. The middleware closes that gap by converting the query string as well.

The reason these are configured rather than switched on globally is that the package does not own the request cycle. It offers classes you install, which is the right shape for a library that a project may want on some endpoints and not others.

Swapping the renderer underneath for orjson or ujson

The renderer wrapped by the camel case layer defaults to rest_framework.renderers.JSONRenderer, and the README explains how to replace it. The candidates it names are drf_orjson_renderer.renderers.ORJSONRenderer, drf_ujson.renderers.UJSONRenderer, and rest_framework.renderers.UnicodeJSONRenderer for DRF versions before 3.0.

The setting is a dictionary under the JSON_CAMEL_CASE key:

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

This is a performance choice rather than a naming one. The standard DRF JSON renderer goes through the ordinary Python encoding path for every object, while orjson and ujson are separate packages built for speed, and the camel case wrapper sits on top of whichever renderer you pick. If you are already swapping in a faster renderer, the two decisions compose rather than conflict.

One rough edge in the README is worth flagging. The paragraph describing the alternatives repeats the words settings file at the end of a sentence, which reads like an editing slip rather than a second instruction, so the whole paragraph is one option, not two.

The practical takeaway is that the renderer class name in your settings determines both how your response is serialised and how quickly it happens, and the camel case conversion is applied on top of it either way.

The digit rule: v2Counter becomes v_2_counter by default

This is the setting most likely to surprise you, and the README is upfront about it. There are two conventions for snake case when a digit is involved, and the package picks the first one:

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

    # Case 2
    v2Counter -> v2_counter
    fooBar2 -> foo_bar2

The difference is whether an underscore is inserted before a digit that follows letters, and the two conventions produce different keys for the same input. That matters if a frontend has already been written against one of them, because the two are not interchangeable at the contract level.

The default is case 1, and the alternative is a settings flag:

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

There is also a class level route, which is the one to reach for when a single view needs the other convention. You subclass the parser and set json_underscoreize on the subclass, then point the view at it:

python
    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,)

That per-view granularity is the useful part of the design. A global setting that changes a key name is a global migration for every client you have, while a parser subclass lets one endpoint speak a different convention from the rest of the API.

ignore_fields and ignore_keys protect different things

The two exclusion options look similar and behave differently, and the README spells out the difference with examples.

ignore_fields names a field whose data should not be changed. The field itself is still renamed, but the recursion stops there, so nothing inside it is converted. The input is:

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

which becomes {"myKey": {"doNotChange": 1}} by default, because the outer key is renamed and the inner dictionary is still walked. With the field listed in the settings:

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

the result is {"myKey": {"do_not_change": 1}}. The key my_key becomes myKey, and the value inside it is left exactly as it was received.

ignore_keys names a key that should not be renamed at all, while the values inside it are still converted. The input is:

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

which becomes {"unchangingKey": {"changeMe": 1}} by default, and becomes {"unchanging_key": {"changeMe": 1}} once the key is in the ignore_keys tuple. The key keeps its original spelling and the nested dictionary is still walked.

So one option protects the contents of a field and the other protects the name of a key, and the README notes that the two can be applied to the same key if you need both. If you are storing an opaque third party payload under a key you do not control, that combination is the configuration you want, because it leaves the sub-structure untouched while keeping the outer contract camelCase.

Packaging, testing and what the project does not claim

The project uses an older style of packaging, which tells you something about its era and audience. setup.py builds the distribution, reads the readme and the history file to form the long description, and pulls the runtime requirements out of requirements.txt at build time with a small helper that skips comment lines and flags. python_requires is 3.7 or later, and the classifiers list Python 3.7 through 3.12 alongside a Development Status of 2 - Pre-Alpha, which is a conservative label for a package with this much use on PyPI.

The runtime requirements are deliberately minimal, djangorestframework of at least 3.13.0 and django of at least 3.2, and no version constraint is visible for the naming library the README points at, so it is worth checking what gets installed in your own environment, since the naming behaviour you get is delegated to that package.

The test story is thin. The README gives one command, and the repository has a single tests.py at the top level:

bash
    $ python -m unittest discover

The Makefile offers the usual targets, flake8 for linting, a test target that runs setup.py test, a test-all target that runs tox, and a coverage target that produces an HTML report. The presence of tox.ini and requirements.txt suggests the version matrix work is handled there rather than in CI configuration, with a .github directory alongside for workflows.

There are no GitHub releases, so the version history lives in HISTORY.rst, and the last push was on 2026-07-20. On licensing, the README's licence section says free software, BSD license, and setup.py records the same, but GitHub's own licence detection does not classify the LICENSE file and asks you to read it. For a package this widely used, reading that file yourself is the sensible step rather than trusting the label.

Editorial conclusion

Adopt djangorestframework-camel-case if your frontend contract requires camelCase while your Django models and serializers stay in snake_case, and if you would rather put the conversion at the DRF boundary than rewrite every serializer. Do not adopt it if your API is consumed only by Python or by clients that already handle snake_case, since the package adds a layer to every request and response for no gain there. Verify first that the renderer and parser classes you configure are the ones your project actually uses, since the default underneath is rest_framework.renderers.JSONRenderer, that the digit naming matches what your frontend expects, because the default inserts an underscore before a number and the alternative does not, and that the LICENSE file in the repository says what you expect, since the README calls the package BSD while GitHub's own detection does not classify that file.

Frequently asked questions

How do I install and enable djangorestframework-camel-case?

Run pip install djangorestframework-camel-case, then set DEFAULT_RENDERER_CLASSES and DEFAULT_PARSER_CLASSES in your REST_FRAMEWORK settings to the camel case classes, and add djangorestframework_camel_case.middleware.CamelCaseMiddleWare to your MIDDLEWARE list so query string parameters are converted as well.

What happens to a key like v2Counter in djangorestframework-camel-case?

By default it becomes v_2_counter, following the first of the two snake case conventions the README describes. Setting 'no_underscore_before_number': True under JSON_UNDERSCOREIZE in REST_FRAMEWORK, or setting json_underscoreize on a parser subclass, produces v2_counter instead.

What is the difference between ignore_fields and ignore_keys?

ignore_fields stops the recursion, so the named field is still renamed but the data inside it is left unchanged. ignore_keys keeps the named key from being renamed while the values inside it are still converted, and the README notes the two can be applied to the same key.

Can I use orjson or ujson with djangorestframework-camel-case?

Yes. The default renderer underneath is rest_framework.renderers.JSONRenderer, and the README shows how to specify drf_orjson_renderer.renderers.ORJSONRenderer or drf_ujson.renderers.UJSONRenderer through the RENDERER_CLASS key of the JSON_CAMEL_CASE setting.

How do I run the djangorestframework-camel-case tests?

The README gives a single command, running python -m unittest discover from the root of the project. The Makefile also provides a test target that runs setup.py test, a test-all target that runs tox, and a coverage target that produces an HTML report.

Official sources

  1. Issues
  2. README
  3. vbabiy/djangorestframework-camel-case on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vbabiy-djangorestframework-camel-case.svg)](https://hysenlabs.com/projects/vbabiy-djangorestframework-camel-case)

Was this useful?