Skip to content

Commit 3a963a3

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

File tree

2 files changed

+61
-18
lines changed

2 files changed

+61
-18
lines changed

rest_framework_json_api/relations.py

+46-5
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,21 @@
2929
]
3030

3131

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

52-
def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwargs):
67+
def __init__(self, self_link_view_name=None, related_link_view_name=None, render_data=True, **kwargs):
5368
if self_link_view_name is not None:
5469
self.self_link_view_name = self_link_view_name
5570
if related_link_view_name is not None:
@@ -74,6 +89,29 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar
7489

7590
super(ResourceRelatedField, self).__init__(**kwargs)
7691

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

296-
def __init__(self, child_relation=None, *args, **kwargs):
334+
def __init__(self, child_relation=None, render_data=True, *args, **kwargs):
335+
self.render_data = render_data
297336
model = kwargs.pop('model', None)
298337
if child_relation is not None:
299338
self.child_relation = child_relation
@@ -306,11 +345,13 @@ def many_init(cls, *args, **kwargs):
306345
list_kwargs = {k: kwargs.pop(k) for k in LINKS_PARAMS if k in kwargs}
307346
list_kwargs['child_relation'] = cls(*args, **kwargs)
308347
for key in kwargs.keys():
309-
if key in ('model',) + MANY_RELATION_KWARGS:
348+
if key in ('model', 'render_data') + MANY_RELATION_KWARGS:
310349
list_kwargs[key] = kwargs[key]
311350
return cls(**list_kwargs)
312351

313352
def get_attribute(self, instance):
353+
if not self.render_data:
354+
raise SkipField
314355
# check for a source fn defined on the serializer instead of the model
315356
if self.source and hasattr(self.parent, self.source):
316357
serializer_method = getattr(self.parent, self.source)

rest_framework_json_api/renderers.py

+15-13
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)