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 325f3b9

Browse files
committed
Merge pull request #152 from scottfisk/feature/compound_documents
Added the ability to specify a resource_name on Models
2 parents 42e0554 + 836391f commit 325f3b9

File tree

9 files changed

+185
-29
lines changed

9 files changed

+185
-29
lines changed

‎example/serializers.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def get_suggested(self, obj):
3535
class Meta:
3636
model = Entry
3737
fields = ('blog', 'headline', 'body_text', 'pub_date', 'mod_date',
38-
'authors', 'comments', 'suggested',)
38+
'authors', 'comments', 'suggested',)
3939

4040

4141
class AuthorSerializer(serializers.ModelSerializer):
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import pytest
2+
from django.core.urlresolvers import reverse
3+
4+
from example.tests.utils import load_json
5+
6+
from example import models, serializers, views
7+
pytestmark = pytest.mark.django_db
8+
9+
10+
class _PatchedModel:
11+
class JSONAPIMeta:
12+
resource_name = "resource_name_from_JSONAPIMeta"
13+
14+
15+
def _check_resource_and_relationship_comment_type_match(django_client):
16+
entry_response = django_client.get(reverse("entry-list"))
17+
comment_response = django_client.get(reverse("comment-list"))
18+
19+
comment_resource_type = load_json(comment_response.content).get('data')[0].get('type')
20+
comment_relationship_type = load_json(entry_response.content).get(
21+
'data')[0].get('relationships').get('comments').get('data')[0].get('type')
22+
23+
assert comment_resource_type == comment_relationship_type, "The resource type seen in the relationships and head resource do not match"
24+
25+
26+
def _check_relationship_and_included_comment_type_are_the_same(django_client, url):
27+
response = django_client.get(url + "?include=comments")
28+
data = load_json(response.content).get('data')[0]
29+
comment = load_json(response.content).get('included')[0]
30+
31+
comment_relationship_type = data.get('relationships').get('comments').get('data')[0].get('type')
32+
comment_included_type = comment.get('type')
33+
34+
assert comment_relationship_type == comment_included_type, "The resource type seen in the relationships and included do not match"
35+
36+
37+
@pytest.mark.usefixtures("single_entry")
38+
class TestModelResourceName:
39+
40+
def test_model_resource_name_on_list(self, client):
41+
models.Comment.__bases__ += (_PatchedModel,)
42+
response = client.get(reverse("comment-list"))
43+
data = load_json(response.content)['data'][0]
44+
# name should be super-author instead of model name RenamedAuthor
45+
assert (data.get('type') == 'resource_name_from_JSONAPIMeta'), (
46+
'resource_name from model incorrect on list')
47+
48+
# Precedence tests
49+
def test_resource_name_precendence(self, client):
50+
# default
51+
response = client.get(reverse("comment-list"))
52+
data = load_json(response.content)['data'][0]
53+
assert (data.get('type') == 'comments'), (
54+
'resource_name from model incorrect on list')
55+
56+
# model > default
57+
models.Comment.__bases__ += (_PatchedModel,)
58+
response = client.get(reverse("comment-list"))
59+
data = load_json(response.content)['data'][0]
60+
assert (data.get('type') == 'resource_name_from_JSONAPIMeta'), (
61+
'resource_name from model incorrect on list')
62+
63+
# serializer > model
64+
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
65+
response = client.get(reverse("comment-list"))
66+
data = load_json(response.content)['data'][0]
67+
assert (data.get('type') == 'resource_name_from_serializer'), (
68+
'resource_name from serializer incorrect on list')
69+
70+
# view > serializer > model
71+
views.CommentViewSet.resource_name = 'resource_name_from_view'
72+
response = client.get(reverse("comment-list"))
73+
data = load_json(response.content)['data'][0]
74+
assert (data.get('type') == 'resource_name_from_view'), (
75+
'resource_name from view incorrect on list')
76+
77+
def teardown_method(self, method):
78+
models.Comment.__bases__ = (models.Comment.__bases__[0],)
79+
try:
80+
delattr(serializers.CommentSerializer.Meta, "resource_name")
81+
except AttributeError:
82+
pass
83+
try:
84+
delattr(views.CommentViewSet, "resource_name")
85+
except AttributeError:
86+
pass
87+
88+
89+
@pytest.mark.usefixtures("single_entry")
90+
class TestResourceNameConsistency:
91+
92+
# Included rename tests
93+
def test_type_match_on_included_and_inline_base(self, client):
94+
_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))
95+
96+
def test_type_match_on_included_and_inline_with_JSONAPIMeta(self, client):
97+
models.Comment.__bases__ += (_PatchedModel,)
98+
99+
_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))
100+
101+
def test_type_match_on_included_and_inline_with_serializer_resource_name(self, client):
102+
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
103+
104+
_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))
105+
106+
def test_type_match_on_included_and_inline_with_serializer_resource_name_and_JSONAPIMeta(self, client):
107+
models.Comment.__bases__ += (_PatchedModel,)
108+
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
109+
110+
_check_relationship_and_included_comment_type_are_the_same(client, reverse("entry-list"))
111+
112+
# Relation rename tests
113+
def test_resource_and_relationship_type_match(self, client):
114+
_check_resource_and_relationship_comment_type_match(client)
115+
116+
def test_resource_and_relationship_type_match_with_serializer_resource_name(self, client):
117+
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
118+
119+
_check_resource_and_relationship_comment_type_match(client)
120+
121+
def test_resource_and_relationship_type_match_with_JSONAPIMeta(self, client):
122+
models.Comment.__bases__ += (_PatchedModel,)
123+
124+
_check_resource_and_relationship_comment_type_match(client)
125+
126+
def test_resource_and_relationship_type_match_with_serializer_resource_name_and_JSONAPIMeta(self, client):
127+
models.Comment.__bases__ += (_PatchedModel,)
128+
serializers.CommentSerializer.Meta.resource_name = "resource_name_from_serializer"
129+
130+
_check_resource_and_relationship_comment_type_match(client)
131+
132+
def teardown_method(self, method):
133+
models.Comment.__bases__ = (models.Comment.__bases__[0],)
134+
try:
135+
delattr(serializers.CommentSerializer.Meta, "resource_name")
136+
except AttributeError:
137+
pass

‎example/urls_test.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
from rest_framework import routers
33

44
from example.views import BlogViewSet, EntryViewSet, AuthorViewSet, EntryRelationshipView, BlogRelationshipView, \
5-
CommentRelationshipView, AuthorRelationshipView
5+
CommentRelationshipView, AuthorRelationshipView, CommentViewSet
66
from .api.resources.identity import Identity, GenericIdentity
77

88
router = routers.DefaultRouter(trailing_slash=False)
99

1010
router.register(r'blogs', BlogViewSet)
1111
router.register(r'entries', EntryViewSet)
1212
router.register(r'authors', AuthorViewSet)
13+
router.register(r'comments', CommentViewSet)
1314

1415
# for the old tests
1516
router.register(r'identities', Identity)

‎example/views.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,3 @@ class CommentRelationshipView(RelationshipView):
4141
class AuthorRelationshipView(RelationshipView):
4242
queryset = Author.objects.all()
4343
self_link_view_name = 'author-relationships'
44-

‎rest_framework_json_api/relations.py‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
from django.utils.translation import ugettext_lazy as _
66

77
from rest_framework_json_api.exceptions import Conflict
8-
from rest_framework_json_api.utils import format_relation_name, Hyperlink, \
9-
get_resource_type_from_queryset, get_resource_type_from_instance
8+
from rest_framework_json_api.utils import Hyperlink, \
9+
get_resource_type_from_queryset, get_resource_type_from_instance, \
10+
get_included_serializers, get_resource_type_from_serializer
1011

1112

1213
class ResourceRelatedField(PrimaryKeyRelatedField):
@@ -127,7 +128,18 @@ def to_representation(self, value):
127128
else:
128129
pk = value.pk
129130

130-
return OrderedDict([('type', format_relation_name(get_resource_type_from_instance(value))), ('id', str(pk))])
131+
# check to see if this resource has a different resource_name when
132+
# included and use that name
133+
resource_type = None
134+
root = getattr(self.parent, 'parent', self.parent)
135+
field_name = self.field_name if self.field_name else self.parent.field_name
136+
if getattr(root, 'included_serializers', None) is not None:
137+
includes = get_included_serializers(root)
138+
if field_name in includes.keys():
139+
resource_type = get_resource_type_from_serializer(includes[field_name])
140+
141+
resource_type = resource_type if resource_type else get_resource_type_from_instance(value)
142+
return OrderedDict([('type', resource_type), ('id', str(pk))])
131143

132144
@property
133145
def choices(self):

‎rest_framework_json_api/renderers.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,7 @@ def extract_included(fields, resource, resource_instance, included_resources):
276276

277277
if isinstance(field, ListSerializer):
278278
serializer = field.child
279-
model = serializer.Meta.model
280-
relation_type = utils.format_relation_name(model.__name__)
279+
relation_type = utils.get_resource_type_from_serializer(serializer)
281280
relation_queryset = list(relation_instance_or_manager.all())
282281

283282
# Get the serializer fields
@@ -298,15 +297,16 @@ def extract_included(fields, resource, resource_instance, included_resources):
298297
)
299298

300299
if isinstance(field, ModelSerializer):
301-
model=field.Meta.model
302-
relation_type = utils.format_relation_name(model.__name__)
300+
301+
relation_type = utils.get_resource_type_from_serializer(field)
303302

304303
# Get the serializer fields
305304
serializer_fields = utils.get_serializer_fields(field)
306305
if serializer_data:
307306
included_data.append(
308-
JSONRenderer.build_json_resource_obj(serializer_fields, serializer_data, relation_instance_or_manager,
309-
relation_type)
307+
JSONRenderer.build_json_resource_obj(
308+
serializer_fields, serializer_data,
309+
relation_instance_or_manager, relation_type)
310310
)
311311
included_data.extend(
312312
JSONRenderer.extract_included(

‎rest_framework_json_api/serializers.py‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
from rest_framework.serializers import *
44

55
from rest_framework_json_api.relations import ResourceRelatedField
6-
from rest_framework_json_api.utils import format_relation_name, get_resource_type_from_instance, \
7-
get_resource_type_from_serializer, get_included_serializers
6+
from rest_framework_json_api.utils import (
7+
get_resource_type_from_model, get_resource_type_from_instance,
8+
get_resource_type_from_serializer, get_included_serializers)
89

910

1011
class ResourceIdentifierObjectSerializer(BaseSerializer):
@@ -24,12 +25,12 @@ def __init__(self, *args, **kwargs):
2425

2526
def to_representation(self, instance):
2627
return {
27-
'type': format_relation_name(get_resource_type_from_instance(instance)),
28+
'type': get_resource_type_from_instance(instance),
2829
'id': str(instance.pk)
2930
}
3031

3132
def to_internal_value(self, data):
32-
if data['type'] != format_relation_name(self.model_class.__name__):
33+
if data['type'] != get_resource_type_from_model(self.model_class):
3334
self.fail('incorrect_model_type', model_type=self.model_class, received_type=data['type'])
3435
pk = data['id']
3536
try:

‎rest_framework_json_api/utils.py‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def get_resource_name(context):
5050
return get_resource_type_from_serializer(serializer)
5151
except AttributeError:
5252
try:
53-
resource_name = view.model.__name__
53+
resource_name = get_resource_type_from_model(view.model)
5454
except AttributeError:
5555
resource_name = view.__class__.__name__
5656

@@ -171,7 +171,7 @@ def get_related_resource_type(relation):
171171
relation_model = parent_model_relation.field.related.model
172172
else:
173173
return get_related_resource_type(parent_model_relation)
174-
return format_relation_name(relation_model.__name__)
174+
return get_resource_type_from_model(relation_model)
175175

176176

177177
def get_instance_or_manager_resource_type(resource_instance_or_manager):
@@ -182,25 +182,31 @@ def get_instance_or_manager_resource_type(resource_instance_or_manager):
182182
pass
183183

184184

185+
def get_resource_type_from_model(model):
186+
json_api_meta = getattr(model, 'JSONAPIMeta', None)
187+
return getattr(
188+
json_api_meta,
189+
'resource_name',
190+
format_relation_name(model.__name__))
191+
192+
185193
def get_resource_type_from_queryset(qs):
186-
return format_relation_name(qs.model._meta.model.__name__)
194+
return get_resource_type_from_model(qs.model)
187195

188196

189197
def get_resource_type_from_instance(instance):
190-
return format_relation_name(instance._meta.model.__name__)
198+
return get_resource_type_from_model(instance._meta.model)
191199

192200

193201
def get_resource_type_from_manager(manager):
194-
return format_relation_name(manager.model.__name__)
202+
return get_resource_type_from_model(manager.model)
195203

196204

197205
def get_resource_type_from_serializer(serializer):
198-
try:
199-
# Check the meta class for resource_name
200-
return serializer.Meta.resource_name
201-
except AttributeError:
202-
# Use the serializer model then pluralize and format
203-
return format_relation_name(serializer.Meta.model.__name__)
206+
return getattr(
207+
serializer.Meta,
208+
'resource_name',
209+
get_resource_type_from_model(serializer.Meta.model))
204210

205211

206212
def get_included_serializers(serializer):

‎rest_framework_json_api/views.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from rest_framework_json_api.exceptions import Conflict
1414
from rest_framework_json_api.serializers import ResourceIdentifierObjectSerializer
15-
from rest_framework_json_api.utils import format_relation_name, get_resource_type_from_instance, OrderedDict, Hyperlink
15+
from rest_framework_json_api.utils import get_resource_type_from_instance, OrderedDict, Hyperlink
1616

1717

1818
class RelationshipView(generics.GenericAPIView):
@@ -154,7 +154,7 @@ def _instantiate_serializer(self, instance):
154154
def get_resource_name(self):
155155
if not hasattr(self, '_resource_name'):
156156
instance = getattr(self.get_object(), self.kwargs['related_field'])
157-
self._resource_name = format_relation_name(get_resource_type_from_instance(instance))
157+
self._resource_name = get_resource_type_from_instance(instance)
158158
return self._resource_name
159159

160160
def set_resource_name(self, value):

0 commit comments

Comments
(0)

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