Skip to content

Commit 07505de

Browse files
committed
Preserve field names when no formatting is configured
This way it is possible to have field names which do not follow the python underscore convention.
1 parent 51daed1 commit 07505de

File tree

11 files changed

+182
-57
lines changed

11 files changed

+182
-57
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ any parts of the framework not mentioned in the documentation should generally b
1414

1515
* Added support for Django 3.2.
1616

17+
### Fixed
18+
19+
* Preserve field names when no formatting is configured.
20+
21+
### Deprecated
22+
23+
* Deprecated default `format_type` argument of `rest_framework_json_api.utils.format_value`. Use `rest_framework_json_api.utils.format_field_name` or specify specifc `format_type` instead.
24+
* Deprecated `format_type` argument of `rest_framework_json_api.utils.format_link_segment`. Use `format_value` instead.
25+
1726
## [4.1.0] - 2021-03-08
1827

1928
### Added

rest_framework_json_api/django_filters/backends.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from rest_framework.exceptions import ValidationError
55
from rest_framework.settings import api_settings
66

7-
from rest_framework_json_api.utils import format_value
7+
from rest_framework_json_api.utils import undo_format_field_name
88

99

1010
class DjangoFilterBackend(DjangoFilterBackend):
@@ -119,8 +119,7 @@ def get_filterset_kwargs(self, request, queryset, view):
119119
)
120120
# convert jsonapi relationship path to Django ORM's __ notation
121121
key = m.groupdict()["assoc"].replace(".", "__")
122-
# undo JSON_API_FORMAT_FIELD_NAMES conversion:
123-
key = format_value(key, "underscore")
122+
key = undo_format_field_name(key)
124123
data.setlist(key, val)
125124
filter_keys.append(key)
126125
del data[qp]

rest_framework_json_api/filters.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from rest_framework.exceptions import ValidationError
44
from rest_framework.filters import BaseFilterBackend, OrderingFilter
55

6-
from rest_framework_json_api.utils import format_value
6+
from rest_framework_json_api.utils import undo_format_field_name
77

88

99
class OrderingFilter(OrderingFilter):
@@ -15,7 +15,7 @@ class OrderingFilter(OrderingFilter):
1515
:py:class:`rest_framework.filters.OrderingFilter` with
1616
:py:attr:`~rest_framework.filters.OrderingFilter.ordering_param` = "sort"
1717
18-
Also applies DJA format_value() to convert (e.g. camelcase) to underscore.
18+
Also supports undo of field name formatting
1919
(See JSON_API_FORMAT_FIELD_NAMES in docs/usage.md)
2020
"""
2121

@@ -38,7 +38,7 @@ def remove_invalid_fields(self, queryset, fields, view, request):
3838
bad_terms = [
3939
term
4040
for term in fields
41-
if format_value(term.replace(".", "__").lstrip("-"), "underscore")
41+
if undo_format_field_name(term.replace(".", "__").lstrip("-"))
4242
not in valid_fields
4343
]
4444
if bad_terms:
@@ -56,10 +56,10 @@ def remove_invalid_fields(self, queryset, fields, view, request):
5656
item_rewritten = item.replace(".", "__")
5757
if item_rewritten.startswith("-"):
5858
underscore_fields.append(
59-
"-" + format_value(item_rewritten.lstrip("-"), "underscore")
59+
"-" + undo_format_field_name(item_rewritten.lstrip("-"))
6060
)
6161
else:
62-
underscore_fields.append(format_value(item_rewritten, "underscore"))
62+
underscore_fields.append(undo_format_field_name(item_rewritten))
6363

6464
return super(OrderingFilter, self).remove_invalid_fields(
6565
queryset, underscore_fields, view, request

rest_framework_json_api/metadata.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from rest_framework.settings import api_settings
88
from rest_framework.utils.field_mapping import ClassLookupDict
99

10-
from rest_framework_json_api.utils import format_value, get_related_resource_type
10+
from rest_framework_json_api.utils import format_field_name, get_related_resource_type
1111

1212

1313
class JSONAPIMetadata(SimpleMetadata):
@@ -93,7 +93,7 @@ def get_serializer_info(self, serializer):
9393

9494
return OrderedDict(
9595
[
96-
(format_value(field_name), self.get_field_info(field))
96+
(format_field_name(field_name), self.get_field_info(field))
9797
for field_name, field in serializer.fields.items()
9898
]
9999
)

rest_framework_json_api/parsers.py

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from rest_framework import parsers
55
from rest_framework.exceptions import ParseError
66

7-
from . import exceptions, renderers, serializers, utils
8-
from .settings import json_api_settings
7+
from rest_framework_json_api import exceptions, renderers, serializers
8+
from rest_framework_json_api.utils import get_resource_name, undo_format_field_names
99

1010

1111
class JSONParser(parsers.JSONParser):
@@ -37,27 +37,13 @@ class JSONParser(parsers.JSONParser):
3737

3838
@staticmethod
3939
def parse_attributes(data):
40-
attributes = data.get("attributes")
41-
uses_format_translation = json_api_settings.FORMAT_FIELD_NAMES
42-
43-
if not attributes:
44-
return dict()
45-
elif uses_format_translation:
46-
# convert back to python/rest_framework's preferred underscore format
47-
return utils.format_field_names(attributes, "underscore")
48-
else:
49-
return attributes
40+
attributes = data.get("attributes") or dict()
41+
return undo_format_field_names(attributes)
5042

5143
@staticmethod
5244
def parse_relationships(data):
53-
uses_format_translation = json_api_settings.FORMAT_FIELD_NAMES
54-
relationships = data.get("relationships")
55-
56-
if not relationships:
57-
relationships = dict()
58-
elif uses_format_translation:
59-
# convert back to python/rest_framework's preferred underscore format
60-
relationships = utils.format_field_names(relationships, "underscore")
45+
relationships = data.get("relationships") or dict()
46+
relationships = undo_format_field_names(relationships)
6147

6248
# Parse the relationships
6349
parsed_relationships = dict()
@@ -130,7 +116,7 @@ def parse(self, stream, media_type=None, parser_context=None):
130116

131117
# Check for inconsistencies
132118
if request.method in ("PUT", "POST", "PATCH"):
133-
resource_name = utils.get_resource_name(
119+
resource_name = get_resource_name(
134120
parser_context, expand_polymorphic_types=True
135121
)
136122
if isinstance(resource_name, str):

rest_framework_json_api/utils.py

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import copy
22
import inspect
33
import operator
4+
import warnings
45
from collections import OrderedDict
56

67
import inflection
@@ -118,8 +119,76 @@ def format_field_names(obj, format_type=None):
118119
return obj
119120

120121

122+
def undo_format_field_names(obj):
123+
"""
124+
Takes a dict and undo format field names to underscore which is the Python convention
125+
but only in case `JSON_API_FORMAT_FIELD_NAMES` is actually configured.
126+
"""
127+
if json_api_settings.FORMAT_FIELD_NAMES:
128+
return format_field_names(obj, "underscore")
129+
130+
return obj
131+
132+
133+
def format_field_name(field_name):
134+
"""
135+
Takes a field name and returns it with formatted keys as set in
136+
`JSON_API_FORMAT_FIELD_NAMES`
137+
"""
138+
return format_value(field_name, json_api_settings.FORMAT_FIELD_NAMES)
139+
140+
141+
def undo_format_field_name(field_name):
142+
"""
143+
Takes a string and undos format field name to underscore which is the Python convention
144+
but only in case `JSON_API_FORMAT_FIELD_NAMES` is actually configured.
145+
"""
146+
if json_api_settings.FORMAT_FIELD_NAMES:
147+
return format_value(field_name, "underscore")
148+
149+
return field_name
150+
151+
152+
def format_link_segment(value, format_type=None):
153+
"""
154+
Takes a string value and returns it with formatted keys as set in `format_type`
155+
or `JSON_API_FORMAT_RELATED_LINKS`.
156+
157+
:format_type: Either 'dasherize', 'camelize', 'capitalize' or 'underscore'
158+
"""
159+
if format_type is None:
160+
format_type = json_api_settings.FORMAT_RELATED_LINKS
161+
else:
162+
warnings.warn(
163+
DeprecationWarning(
164+
"Using `format_type` argument is deprecated."
165+
"Use `format_value` instead."
166+
)
167+
)
168+
169+
return format_value(value, format_type)
170+
171+
172+
def undo_format_link_segment(value):
173+
"""
174+
Takes a link segment and undos format link segment to underscore which is the Python convention
175+
but only in case `JSON_API_FORMAT_RELATED_LINKS` is actually configured.
176+
"""
177+
178+
if json_api_settings.FORMAT_RELATED_LINKS:
179+
return format_value(value, "underscore")
180+
181+
return value
182+
183+
121184
def format_value(value, format_type=None):
122185
if format_type is None:
186+
warnings.warn(
187+
DeprecationWarning(
188+
"Using `format_value` without passing on `format_type` argument is deprecated."
189+
"Use `format_field_name` instead."
190+
)
191+
)
123192
format_type = json_api_settings.FORMAT_FIELD_NAMES
124193
if format_type == "dasherize":
125194
# inflection can't dasherize camelCase
@@ -142,25 +211,11 @@ def format_resource_type(value, format_type=None, pluralize=None):
142211
pluralize = json_api_settings.PLURALIZE_TYPES
143212

144213
if format_type:
145-
# format_type will never be None here so we can use format_value
146214
value = format_value(value, format_type)
147215

148216
return inflection.pluralize(value) if pluralize else value
149217

150218

151-
def format_link_segment(value, format_type=None):
152-
"""
153-
Takes a string value and returns it with formatted keys as set in `format_type`
154-
or `JSON_API_FORMAT_RELATED_LINKS`.
155-
156-
:format_type: Either 'dasherize', 'camelize', 'capitalize' or 'underscore'
157-
"""
158-
if format_type is None:
159-
format_type = json_api_settings.FORMAT_RELATED_LINKS
160-
161-
return format_value(value, format_type)
162-
163-
164219
def get_related_resource_type(relation):
165220
from rest_framework_json_api.serializers import PolymorphicModelSerializer
166221

@@ -348,7 +403,7 @@ def format_drf_errors(response, context, exc):
348403
# handle all errors thrown from serializers
349404
else:
350405
for field, error in response.data.items():
351-
field = format_value(field)
406+
field = format_field_name(field)
352407
pointer = "/data/attributes/{}".format(field)
353408
if isinstance(exc, Http404) and isinstance(error, str):
354409
# 404 errors don't have a pointer

rest_framework_json_api/views.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
from rest_framework_json_api.utils import (
2626
Hyperlink,
2727
OrderedDict,
28-
format_value,
2928
get_included_resources,
3029
get_resource_type_from_instance,
30+
undo_format_link_segment,
3131
)
3232

3333

@@ -187,7 +187,7 @@ def get_related_serializer_class(self):
187187

188188
def get_related_field_name(self):
189189
field_name = self.kwargs["related_field"]
190-
return format_value(field_name, "underscore")
190+
return undo_format_link_segment(field_name)
191191

192192
def get_related_instance(self):
193193
parent_obj = self.get_object()

tests/test_parsers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def parser_context(self, rf):
2929
@pytest.mark.parametrize(
3030
"format_field_names",
3131
[
32-
None,
32+
False,
3333
"dasherize",
3434
"camelize",
3535
"capitalize",

tests/test_relations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ def test_to_representation(self, model, field):
233233
@pytest.mark.parametrize(
234234
"format_related_links",
235235
[
236-
None,
236+
False,
237237
"dasherize",
238238
"camelize",
239239
"capitalize",

0 commit comments

Comments
 (0)