Skip to content

Commit 87108d4

Browse files
authored
Ensured that include param is properly underscored (#1283)
nsured that interpreting `include` query parameter is done in internal Python naming.
1 parent 0394cf9 commit 87108d4

File tree

6 files changed

+43
-9
lines changed

6 files changed

+43
-9
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ any parts of the framework not mentioned in the documentation should generally b
1515
* Added support for Django REST framework 3.16.
1616
* Added support for Django 5.2.
1717

18+
### Fixed
19+
20+
* Ensured that interpreting `include` query parameter is done in internal Python naming.
21+
This adds full support for using multipart field names for includes while configuring `JSON_API_FORMAT_FIELD_NAMES`.
22+
1823
### Removed
1924

2025
* Removed support for Python 3.8.

rest_framework_json_api/renderers.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from collections import defaultdict
77
from collections.abc import Iterable
88

9-
import inflection
109
from django.db.models import Manager
1110
from django.template import loader
1211
from django.utils.encoding import force_str
@@ -277,9 +276,6 @@ def extract_included(
277276
current_serializer, "included_serializers", dict()
278277
)
279278
included_resources = copy.copy(included_resources)
280-
included_resources = [
281-
inflection.underscore(value) for value in included_resources
282-
]
283279

284280
for field_name, field in iter(fields.items()):
285281
# Skip URL field

rest_framework_json_api/serializers.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from collections.abc import Mapping
22

3-
import inflection
43
from django.core.exceptions import ObjectDoesNotExist
54
from django.db.models.query import QuerySet
65
from django.utils.module_loading import import_string as import_class_from_dotted_path
@@ -129,7 +128,7 @@ def validate_path(serializer_class, field_path, path):
129128
serializers = getattr(serializer_class, "included_serializers", None)
130129
if serializers is None:
131130
raise ParseError("This endpoint does not support the include parameter")
132-
this_field_name = inflection.underscore(field_path[0])
131+
this_field_name = field_path[0]
133132
this_included_serializer = serializers.get(this_field_name)
134133
if this_included_serializer is None:
135134
raise ParseError(

rest_framework_json_api/utils.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,10 +316,18 @@ def get_resource_id(resource_instance, resource):
316316

317317

318318
def get_included_resources(request, serializer=None):
319-
"""Build a list of included resources."""
319+
"""
320+
Build a list of included resources.
321+
322+
This method ensures that returned includes are in Python internally used
323+
format.
324+
"""
320325
include_resources_param = request.query_params.get("include") if request else None
321326
if include_resources_param:
322-
return include_resources_param.split(",")
327+
return [
328+
undo_format_field_name(include)
329+
for include in include_resources_param.split(",")
330+
]
323331
else:
324332
return get_default_included_resources_from_serializer(serializer)
325333

tests/conftest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import pytest
2-
from rest_framework.test import APIClient
2+
from rest_framework.test import APIClient, APIRequestFactory
33

44
from tests.models import (
55
BasicModel,
@@ -98,3 +98,8 @@ def nested_related_source(
9898
@pytest.fixture
9999
def client():
100100
return APIClient()
101+
102+
103+
@pytest.fixture
104+
def rf():
105+
return APIRequestFactory()

tests/test_utils.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from rest_framework import status
33
from rest_framework.fields import Field
44
from rest_framework.generics import GenericAPIView
5+
from rest_framework.request import Request
56
from rest_framework.response import Response
67
from rest_framework.views import APIView
78

@@ -13,6 +14,7 @@
1314
format_link_segment,
1415
format_resource_type,
1516
format_value,
17+
get_included_resources,
1618
get_related_resource_type,
1719
get_resource_id,
1820
get_resource_name,
@@ -456,3 +458,22 @@ def test_get_resource_id(resource_instance, resource, expected):
456458
)
457459
def test_format_error_object(message, pointer, response, result):
458460
assert result == format_error_object(message, pointer, response)
461+
462+
463+
@pytest.mark.parametrize(
464+
"format_type,include_param,expected_includes",
465+
[
466+
("dasherize", "author-bio", ["author_bio"]),
467+
("dasherize", "author-bio,author-type", ["author_bio", "author_type"]),
468+
("dasherize", "author-bio.author-type", ["author_bio.author_type"]),
469+
("camelize", "authorBio", ["author_bio"]),
470+
],
471+
)
472+
def test_get_included_resources(
473+
rf, include_param, expected_includes, format_type, settings
474+
):
475+
settings.JSON_API_FORMAT_FIELD_NAMES = format_type
476+
477+
request = Request(rf.get("/test/", {"include": include_param}))
478+
includes = get_included_resources(request)
479+
assert includes == expected_includes

0 commit comments

Comments
 (0)