Skip to content

Allow HyperlinkedRelatedField to be used with related urls #529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ santiavenda <[email protected]>
Tim Selman <[email protected]>
Yaniv Peer <[email protected]>
Mohammed Ali Zubair <[email protected]>
Jason Housley <[email protected]>
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ any parts of the framework not mentioned in the documentation should generally b
* Avoid error with related urls when retrieving relationship which is referenced as `ForeignKey` on parent
* Do not render `write_only` relations
* Do not skip empty one-to-one relationships
* Allow `HyperlinkRelatedField` to be used with [related urls](https://django-rest-framework-json-api.readthedocs.io/en/stable/usage.html?highlight=related%20links#related-urls)


## [2.6.0] - 2018-09-20
Expand Down
1 change: 1 addition & 0 deletions example/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class Comment(BaseModel):
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='comments',
)

def __str__(self):
Expand Down
9 changes: 8 additions & 1 deletion example/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,20 +219,27 @@ class AuthorSerializer(serializers.ModelSerializer):
read_only=True,
source='get_first_entry'
)
comments = relations.HyperlinkedRelatedField(
related_link_view_name='author-related',
self_link_view_name='author-relationships',
queryset=Comment.objects,
many=True
)
included_serializers = {
'bio': AuthorBioSerializer,
'type': AuthorTypeSerializer
}
related_serializers = {
'bio': 'example.serializers.AuthorBioSerializer',
'type': 'example.serializers.AuthorTypeSerializer',
'comments': 'example.serializers.CommentSerializer',
'entries': 'example.serializers.EntrySerializer',
'first_entry': 'example.serializers.EntrySerializer'
}

class Meta:
model = Author
fields = ('name', 'email', 'bio', 'entries', 'first_entry', 'type')
fields = ('name', 'email', 'bio', 'entries', 'comments', 'first_entry', 'type')

def get_first_entry(self, obj):
return obj.entries.first()
Expand Down
2 changes: 1 addition & 1 deletion example/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
JSON_API_FORMAT_TYPES = 'camelize'
JSON_API_PLURALIZE_TYPES = True

REST_FRAMEWORK.update({
REST_FRAMEWORK.update({ # noqa
'PAGE_SIZE': 1,
})
22 changes: 16 additions & 6 deletions example/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ def test_get_empty_to_one_relationship(self):
assert response.data == expected_data

def test_get_to_many_relationship_self_link(self):
url = '/authors/{}/relationships/comment_set'.format(self.author.id)
url = '/authors/{}/relationships/comments'.format(self.author.id)

response = self.client.get(url)
expected_data = {
'links': {'self': 'http://testserver/authors/1/relationships/comment_set'},
'links': {'self': 'http://testserver/authors/1/relationships/comments'},
'data': [{'id': str(self.second_comment.id), 'type': format_resource_type('Comment')}]
}
assert json.loads(response.content.decode('utf-8')) == expected_data
Expand Down Expand Up @@ -222,7 +222,7 @@ def test_delete_one_to_many_relationship_with_not_null_constraint(self):
assert response.status_code == 409, response.content.decode()

def test_delete_to_many_relationship_with_change(self):
url = '/authors/{}/relationships/comment_set'.format(self.author.id)
url = '/authors/{}/relationships/comments'.format(self.author.id)
request_data = {
'data': [{'type': format_resource_type('Comment'), 'id': str(self.second_comment.id)}, ]
}
Expand All @@ -233,7 +233,7 @@ def test_new_comment_data_patch_to_many_relationship(self):
entry = EntryFactory(blog=self.blog, authors=(self.author,))
comment = CommentFactory(entry=entry)

url = '/authors/{}/relationships/comment_set'.format(self.author.id)
url = '/authors/{}/relationships/comments'.format(self.author.id)
request_data = {
'data': [{'type': format_resource_type('Comment'), 'id': str(comment.id)}, ]
}
Expand All @@ -244,7 +244,7 @@ def test_new_comment_data_patch_to_many_relationship(self):
}
],
'links': {
'self': 'http://testserver/authors/{}/relationships/comment_set'.format(
'self': 'http://testserver/authors/{}/relationships/comments'.format(
self.author.id
)
}
Expand All @@ -261,7 +261,7 @@ def test_new_comment_data_patch_to_many_relationship(self):
}
],
'links': {
'self': 'http://testserver/authors/{}/relationships/comment_set'.format(
'self': 'http://testserver/authors/{}/relationships/comments'.format(
self.author.id
)
}
Expand Down Expand Up @@ -369,6 +369,16 @@ def test_retrieve_related_many(self):
self.assertEqual(len(resp.json()['data']), 1)
self.assertEqual(resp.json()['data'][0]['id'], str(entry.id))

def test_retrieve_related_many_hyperlinked(self):
comment = CommentFactory(author=self.author)
url = reverse('author-related', kwargs={'pk': self.author.pk, 'related_field': 'comments'})
resp = self.client.get(url)

self.assertEqual(resp.status_code, 200)
self.assertTrue(isinstance(resp.json()['data'], list))
self.assertEqual(len(resp.json()['data']), 1)
self.assertEqual(resp.json()['data'][0]['id'], str(comment.id))

def test_retrieve_related_None(self):
kwargs = {'pk': self.author.pk, 'related_field': 'first_entry'}
url = reverse('author-related', kwargs=kwargs)
Expand Down
12 changes: 8 additions & 4 deletions rest_framework_json_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from rest_framework.relations import PKOnlyObject
from rest_framework.response import Response
from rest_framework.reverse import reverse
from rest_framework.serializers import Serializer
from rest_framework.serializers import Serializer, SkipField

from rest_framework_json_api.exceptions import Conflict
from rest_framework_json_api.serializers import ResourceIdentifierObjectSerializer
Expand Down Expand Up @@ -166,10 +166,14 @@ def get_related_instance(self):
field = parent_serializer.fields.get(field_name, None)

if field is not None:
instance = field.get_attribute(parent_obj)
if isinstance(instance, PKOnlyObject):
# need whole object
try:
instance = field.get_attribute(parent_obj)
except SkipField:
instance = get_attribute(parent_obj, field.source_attrs)
else:
if isinstance(instance, PKOnlyObject):
# need whole object
instance = get_attribute(parent_obj, field.source_attrs)
return instance
else:
try:
Expand Down