Skip to content

Commit afc0a79

Browse files
committed
"Data" key optional in relationships
1 parent 79e7738 commit afc0a79

File tree

2 files changed

+62
-18
lines changed

2 files changed

+62
-18
lines changed

rest_framework_json_api/relations.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from django.core.exceptions import ImproperlyConfigured
88
from django.urls import NoReverseMatch
99
from django.utils.translation import ugettext_lazy as _
10-
from rest_framework.fields import MISSING_ERROR_MESSAGE
11-
from rest_framework.relations import MANY_RELATION_KWARGS, PrimaryKeyRelatedField
10+
from rest_framework.fields import MISSING_ERROR_MESSAGE, SkipField
11+
from rest_framework.relations import MANY_RELATION_KWARGS, PrimaryKeyRelatedField, ManyRelatedField as DRFManyRelatedField
1212
from rest_framework.reverse import reverse
1313
from rest_framework.serializers import Serializer
1414

@@ -29,6 +29,22 @@
2929
]
3030

3131

32+
class ManyRelatedField(DRFManyRelatedField):
33+
"""
34+
This workaround skips "data" rendering for relationships
35+
in order to save some sql queries and improve performance
36+
"""
37+
38+
def __init__(self, child_relation=None, *args, **kwargs):
39+
self.render_data = kwargs.pop('render_data', True)
40+
super(ManyRelatedField, self).__init__(child_relation, *args, **kwargs)
41+
42+
def get_attribute(self, instance):
43+
if self.render_data:
44+
return super(ManyRelatedField, self).get_attribute(instance)
45+
raise SkipField
46+
47+
3248
class ResourceRelatedField(PrimaryKeyRelatedField):
3349
_skip_polymorphic_optimization = True
3450
self_link_view_name = None
@@ -49,7 +65,7 @@ class ResourceRelatedField(PrimaryKeyRelatedField):
4965
'no_match': _('Invalid hyperlink - No URL match.'),
5066
}
5167

52-
def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwargs):
68+
def __init__(self, self_link_view_name=None, related_link_view_name=None, render_data=True, **kwargs):
5369
if self_link_view_name is not None:
5470
self.self_link_view_name = self_link_view_name
5571
if related_link_view_name is not None:
@@ -74,6 +90,29 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar
7490

7591
super(ResourceRelatedField, self).__init__(**kwargs)
7692

93+
@classmethod
94+
def many_init(cls, *args, **kwargs):
95+
"""
96+
This method handles creating a parent `ManyRelatedField` instance
97+
when the `many=True` keyword argument is passed.
98+
99+
Typically you won't need to override this method.
100+
101+
Note that we're over-cautious in passing most arguments to both parent
102+
and child classes in order to try to cover the general case. If you're
103+
overriding this method you'll probably want something much simpler, eg:
104+
105+
@classmethod
106+
def many_init(cls, *args, **kwargs):
107+
kwargs['child'] = cls()
108+
return CustomManyRelatedField(*args, **kwargs)
109+
"""
110+
list_kwargs = {'child_relation': cls(*args, **kwargs)}
111+
for key in kwargs.keys():
112+
if key in MANY_RELATION_KWARGS + ('render_data',):
113+
list_kwargs[key] = kwargs[key]
114+
return ManyRelatedField(**list_kwargs)
115+
77116
def use_pk_only_optimization(self):
78117
# We need the real object to determine its type...
79118
return self.get_resource_type_from_included_serializer() is not None
@@ -293,7 +332,8 @@ def __new__(cls, *args, **kwargs):
293332
return cls.many_init(*args, **kwargs)
294333
return super(ResourceRelatedField, cls).__new__(cls, *args, **kwargs)
295334

296-
def __init__(self, child_relation=None, *args, **kwargs):
335+
def __init__(self, child_relation=None, render_data=True, *args, **kwargs):
336+
self.render_data = render_data
297337
model = kwargs.pop('model', None)
298338
if child_relation is not None:
299339
self.child_relation = child_relation
@@ -306,11 +346,13 @@ def many_init(cls, *args, **kwargs):
306346
list_kwargs = {k: kwargs.pop(k) for k in LINKS_PARAMS if k in kwargs}
307347
list_kwargs['child_relation'] = cls(*args, **kwargs)
308348
for key in kwargs.keys():
309-
if key in ('model',) + MANY_RELATION_KWARGS:
349+
if key in ('model', 'render_data') + MANY_RELATION_KWARGS:
310350
list_kwargs[key] = kwargs[key]
311351
return cls(**list_kwargs)
312352

313353
def get_attribute(self, instance):
354+
if not self.render_data:
355+
raise SkipField
314356
# check for a source fn defined on the serializer instead of the model
315357
if self.source and hasattr(self.parent, self.source):
316358
serializer_method = getattr(self.parent, self.source)

rest_framework_json_api/renderers.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,12 @@ def extract_relationships(cls, fields, resource, resource_instance):
134134
if not resolved:
135135
continue
136136

137+
relation_data = {}
137138
# special case for ResourceRelatedField
138-
relation_data = {
139-
'data': resource.get(field_name)
140-
}
139+
if field_name in resource:
140+
relation_data.update({
141+
'data': resource.get(field_name)
142+
})
141143

142144
field_links = field.get_links(
143145
resource_instance, field.related_link_lookup_field)
@@ -188,9 +190,15 @@ def extract_relationships(cls, fields, resource, resource_instance):
188190

189191
if isinstance(field.child_relation, ResourceRelatedField):
190192
# special case for ResourceRelatedField
191-
relation_data = {
192-
'data': resource.get(field_name)
193-
}
193+
relation_data = {}
194+
195+
if field_name in resource:
196+
relation_data.update({
197+
'data': resource.get(field_name),
198+
'meta': {
199+
'count': len(resource.get(field_name))
200+
}
201+
})
194202

195203
field_links = field.child_relation.get_links(
196204
resource_instance,
@@ -200,13 +208,7 @@ def extract_relationships(cls, fields, resource, resource_instance):
200208
{'links': field_links}
201209
if field_links else dict()
202210
)
203-
relation_data.update(
204-
{
205-
'meta': {
206-
'count': len(resource.get(field_name))
207-
}
208-
}
209-
)
211+
210212
data.update({field_name: relation_data})
211213
continue
212214

0 commit comments

Comments
 (0)