Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9e395a2

Browse files
slivercmblayman
authored andcommitted
Added nested included serializer support for remapped relations (#347)
* Added nested included serializer support for remapped relations * Added docstring to extract_relation_instance
1 parent 6140662 commit 9e395a2

File tree

5 files changed

+66
-16
lines changed

5 files changed

+66
-16
lines changed

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ v2.3.0
77
* Fix for apps that don't use `django.contrib.contenttypes`.
88
* Fix `resource_name` support for POST requests and nested serializers
99
* Enforcing flake8 linting
10+
* Added nested included serializer support for remapped relations
1011

1112
v2.2.0
1213

‎example/serializers.py‎

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,25 @@ class Meta:
111111
fields = ('name', 'email', 'bio', 'entries')
112112

113113

114+
class WriterSerializer(serializers.ModelSerializer):
115+
included_serializers = {
116+
'bio': AuthorBioSerializer
117+
}
118+
119+
class Meta:
120+
model = Author
121+
fields = ('name', 'email', 'bio')
122+
resource_name = 'writers'
123+
124+
114125
class CommentSerializer(serializers.ModelSerializer):
126+
# testing remapping of related name
127+
writer = relations.ResourceRelatedField(source='author', read_only=True)
128+
115129
included_serializers = {
116130
'entry': EntrySerializer,
117-
'author': AuthorSerializer
131+
'author': AuthorSerializer,
132+
'writer': WriterSerializer
118133
}
119134

120135
class Meta:

‎example/tests/integration/test_includes.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,15 @@ def test_missing_field_not_included(author_bio_factory, author_factory, client):
8282

8383
def test_deep_included_data_on_list(multiple_entries, client):
8484
response = client.get(reverse("entry-list") + '?include=comments,comments.author,'
85-
'comments.author.bio&page_size=5')
85+
'comments.author.bio,comments.writer&page_size=5')
8686
included = load_json(response.content).get('included')
8787

8888
assert len(load_json(response.content)['data']) == len(multiple_entries), (
8989
'Incorrect entry count'
9090
)
9191
assert [x.get('type') for x in included] == [
92-
'authorBios', 'authorBios', 'authors', 'authors', 'comments', 'comments'
92+
'authorBios', 'authorBios', 'authors', 'authors',
93+
'comments', 'comments', 'writers', 'writers'
9394
], 'List included types are incorrect'
9495

9596
comment_count = len([resource for resource in included if resource["type"] == "comments"])
@@ -106,6 +107,13 @@ def test_deep_included_data_on_list(multiple_entries, client):
106107
author__bio__isnull=False).count() for entry in multiple_entries])
107108
assert author_bio_count == expected_author_bio_count, 'List author bio count is incorrect'
108109

110+
writer_count = len(
111+
[resource for resource in included if resource["type"] == "writers"]
112+
)
113+
expected_writer_count = sum(
114+
[entry.comments.filter(author__isnull=False).count() for entry in multiple_entries])
115+
assert writer_count == expected_writer_count, 'List writer count is incorrect'
116+
109117
# Also include entry authors
110118
response = client.get(reverse("entry-list") + '?include=authors,comments,comments.author,'
111119
'comments.author.bio&page_size=5')

‎example/tests/test_serializers.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ def test_model_serializer_with_implicit_fields(self, comment, client):
102102
"id": str(comment.author.pk)
103103
}
104104
},
105+
"writer": {
106+
"data": {
107+
"type": "writers",
108+
"id": str(comment.author.pk)
109+
}
110+
},
105111
}
106112
}
107113
}

‎rest_framework_json_api/renderers.py‎

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,36 @@ def extract_relationships(cls, fields, resource, resource_instance):
272272

273273
return utils.format_keys(data)
274274

275+
@classmethod
276+
def extract_relation_instance(cls, field_name, field, resource_instance, serializer):
277+
"""
278+
Determines what instance represents given relation and extracts it.
279+
280+
Relation instance is determined by given field_name or source configured on
281+
field. As fallback is a serializer method called with name of field's source.
282+
"""
283+
relation_instance = None
284+
285+
try:
286+
relation_instance = getattr(resource_instance, field_name)
287+
except AttributeError:
288+
try:
289+
# For ManyRelatedFields if `related_name` is not set
290+
# we need to access `foo_set` from `source`
291+
relation_instance = getattr(resource_instance, field.child_relation.source)
292+
except AttributeError:
293+
if hasattr(serializer, field.source):
294+
serializer_method = getattr(serializer, field.source)
295+
relation_instance = serializer_method(resource_instance)
296+
else:
297+
# case when source is a simple remap on resource_instance
298+
try:
299+
relation_instance = getattr(resource_instance, field.source)
300+
except AttributeError:
301+
pass
302+
303+
return relation_instance
304+
275305
@classmethod
276306
def extract_included(cls, fields, resource, resource_instance, included_resources):
277307
# this function may be called with an empty record (example: Browsable Interface)
@@ -304,19 +334,9 @@ def extract_included(cls, fields, resource, resource_instance, included_resource
304334
if field_name not in [node.split('.')[0] for node in included_resources]:
305335
continue
306336

307-
try:
308-
relation_instance = getattr(resource_instance, field_name)
309-
except AttributeError:
310-
try:
311-
# For ManyRelatedFields if `related_name` is not set we need to access `foo_set`
312-
# from `source`
313-
relation_instance = getattr(resource_instance, field.child_relation.source)
314-
except AttributeError:
315-
if not hasattr(current_serializer, field.source):
316-
continue
317-
serializer_method = getattr(current_serializer, field.source)
318-
relation_instance = serializer_method(resource_instance)
319-
337+
relation_instance = cls.extract_relation_instance(
338+
field_name, field, resource_instance, current_serializer
339+
)
320340
if isinstance(relation_instance, Manager):
321341
relation_instance = relation_instance.all()
322342

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /