Skip to content

Commit 2797b9d

Browse files
committed
Now supporting links for RelationshipView
1 parent ea65003 commit 2797b9d

File tree

4 files changed

+60
-11
lines changed

4 files changed

+60
-11
lines changed

example/tests/test_views.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,16 @@ def test_get_empty_to_one_relationship(self):
7979

8080
assert response.data == expected_data
8181

82+
def test_get_to_many_relationship_self_link(self):
83+
url = '/authors/{}/relationships/comment_set'.format(self.author.id)
84+
85+
response = self.client.get(url)
86+
expected_data = {
87+
'links': {'self': 'http://testserver/authors/1/relationships/comment_set'},
88+
'data': [{'id': str(self.second_comment.id), 'type': format_relation_name('Comment')}]
89+
}
90+
assert json.loads(response.content.decode('utf-8')) == expected_data
91+
8292
def test_patch_to_one_relationship(self):
8393
url = '/entries/{}/relationships/blog'.format(self.first_entry.id)
8494
request_data = {

example/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ class CommentRelationshipView(RelationshipView):
3535

3636
class AuthorRelationshipView(RelationshipView):
3737
queryset = Author.objects
38+
self_link_view_name = 'author-relationships'

rest_framework_json_api/renderers.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
3939
from rest_framework_json_api.views import RelationshipView
4040
if isinstance(view, RelationshipView):
4141
# Special case for RelationshipView
42-
links = view.get_links()
4342
render_data = OrderedDict([
44-
('data', data),
45-
(('links', links) if links else None),
43+
('data', data)
4644
])
45+
links = view.get_links()
46+
if links:
47+
render_data.update({'links': links}),
4748
return super(JSONRenderer, self).render(
4849
render_data, accepted_media_type, renderer_context
4950
)

rest_framework_json_api/views.py

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
from django.core.exceptions import ImproperlyConfigured
2+
from django.core.urlresolvers import NoReverseMatch
13
from django.db.models import Model, QuerySet
24
from django.db.models.manager import BaseManager
35
from rest_framework import generics
6+
from rest_framework.relations import Hyperlink
47
from rest_framework.response import Response
58
from rest_framework.exceptions import NotFound, MethodNotAllowed
9+
from rest_framework.reverse import reverse
610

711
from rest_framework_json_api.exceptions import Conflict
812
from rest_framework_json_api.serializers import ResourceIdentifierObjectSerializer
@@ -11,19 +15,52 @@
1115

1216
class RelationshipView(generics.GenericAPIView):
1317
serializer_class = ResourceIdentifierObjectSerializer
14-
self_view_name = None
15-
related_view_name = None
18+
self_link_view_name = None
19+
related_link_view_name = None
1620

17-
def get_self_link(self):
18-
return 'self_link'
21+
def __init__(self, **kwargs):
22+
super(RelationshipView, self).__init__(**kwargs)
23+
# We include this simply for dependency injection in tests.
24+
# We can't add it as a class attributes or it would expect an
25+
# implicit `self` argument to be passed.
26+
self.reverse = reverse
1927

20-
def get_related_link(self):
21-
return 'related_link'
28+
29+
30+
def get_url(self, name, view_name, kwargs, request):
31+
"""
32+
Given an object, return the URL that hyperlinks to the object.
33+
34+
May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
35+
attributes are not configured to correctly match the URL conf.
36+
"""
37+
38+
# Return None if the view name is not supplied
39+
if not view_name:
40+
return None
41+
42+
# Return the hyperlink, or error if incorrectly configured.
43+
try:
44+
url = self.reverse(view_name, kwargs=kwargs, request=request)
45+
except NoReverseMatch:
46+
msg = (
47+
'Could not resolve URL for hyperlinked relationship using '
48+
'view name "%s". You may have failed to include the related '
49+
'model in your API, or incorrectly configured the '
50+
'`lookup_field` attribute on this field.'
51+
)
52+
raise ImproperlyConfigured(msg % view_name)
53+
54+
if url is None:
55+
return None
56+
57+
return Hyperlink(url, name)
2258

2359
def get_links(self):
2460
return_data = OrderedDict()
25-
self_link = self.get_self_link()
26-
related_link = self.get_related_link()
61+
self_link = self.get_url('self', self.self_link_view_name, self.kwargs, self.request)
62+
related_kwargs = {self.lookup_field: self.kwargs.get(self.lookup_field)}
63+
related_link = self.get_url('related', self.related_link_view_name, related_kwargs, self.request)
2764
if self_link:
2865
return_data.update({'self': self_link})
2966
if related_link:

0 commit comments

Comments
 (0)