Skip to content

Commit b4dffb8

Browse files
santiavenda2mblayman
authored andcommitted
Avoid query explosion for related fields where I can user pk_only_opt… (#374)
* Fix factory boy dependency * fix factory-boy version in setup.py and requirements-development.txt * Fix setup.py factory boy dependency * Avoid query explosion for related fields where I can user pk_only_optimization * Fix autoPrefetchMixin for ReverseOneToOneDescriptor * Fix code style * Avoid query objects in ModelSerializer to_representation method * Fix code queality error * Fix problem that makes None related objects not being renderer * Fix problem that makes None related objects not being renderer * _readable_field property is missing in drf<3.2
1 parent b0257c0 commit b4dffb8

File tree

6 files changed

+67
-18
lines changed

6 files changed

+67
-18
lines changed

example/views.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,11 @@ class AuthorViewSet(ModelViewSet):
7474

7575

7676
class CommentViewSet(ModelViewSet):
77-
queryset = Comment.objects.select_related('author', 'entry')
77+
queryset = Comment.objects.all()
7878
serializer_class = CommentSerializer
7979
prefetch_for_includes = {
8080
'__all__': [],
81-
'author': ['author', 'author__bio', 'author__entries', 'author__type'],
82-
'entry': ['author', 'author__bio', 'author__entries']
81+
'author': ['author__bio', 'author__entries'],
8382
}
8483

8584

requirements-development.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ Sphinx
1313
sphinx_rtd_theme
1414
tox
1515
django-debug-toolbar
16-
packaging==16.8
16+
packaging==16.8

rest_framework_json_api/relations.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar
7676

7777
def use_pk_only_optimization(self):
7878
# We need the real object to determine its type...
79-
return False
79+
return self.get_resource_type_from_included_serializer() is not None
8080

8181
def conflict(self, key, **kwargs):
8282
"""
@@ -251,6 +251,9 @@ def __init__(self, polymorphic_serializer, *args, **kwargs):
251251
self.polymorphic_serializer = polymorphic_serializer
252252
super(PolymorphicResourceRelatedField, self).__init__(*args, **kwargs)
253253

254+
def use_pk_only_optimization(self):
255+
return False
256+
254257
def to_internal_value(self, data):
255258
if isinstance(data, six.text_type):
256259
try:

rest_framework_json_api/renderers.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,12 @@ def extract_relationships(cls, fields, resource, resource_instance):
125125
continue
126126

127127
if isinstance(field, ResourceRelatedField):
128-
resolved, relation_instance = utils.get_relation_instance(
129-
resource_instance, source, field.parent
130-
)
131-
if not resolved:
132-
continue
128+
relation_instance_id = getattr(resource_instance, source + "_id", None)
129+
if not relation_instance_id:
130+
resolved, relation_instance = utils.get_relation_instance(resource_instance,
131+
source, field.parent)
132+
if not resolved:
133+
continue
133134

134135
# special case for ResourceRelatedField
135136
relation_data = {
@@ -256,18 +257,23 @@ def extract_relationships(cls, fields, resource, resource_instance):
256257
continue
257258

258259
if isinstance(field, Serializer):
259-
resolved, relation_instance = utils.get_relation_instance(
260-
resource_instance, source, field.parent
261-
)
262-
if not resolved:
263-
continue
260+
relation_instance_id = getattr(resource_instance, source + "_id", None)
261+
if not relation_instance_id:
262+
resolved, relation_instance = utils.get_relation_instance(
263+
resource_instance, source, field.parent
264+
)
265+
if not resolved:
266+
continue
267+
268+
if relation_instance is not None:
269+
relation_instance_id = relation_instance.pk
264270

265271
data.update({
266272
field_name: {
267273
'data': (
268274
OrderedDict([
269275
('type', relation_type),
270-
('id', encoding.force_text(relation_instance.pk))
276+
('id', encoding.force_text(relation_instance_id))
271277
]) if resource.get(field_name) else None)
272278
}
273279
})

rest_framework_json_api/serializers.py

+41
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,47 @@ def get_field_names(self, declared_fields, info):
170170
fields = super(ModelSerializer, self).get_field_names(declared, info)
171171
return list(fields) + list(getattr(self.Meta, 'meta_fields', list()))
172172

173+
def to_representation(self, instance):
174+
"""
175+
Object instance -> Dict of primitive datatypes.
176+
"""
177+
ret = OrderedDict()
178+
readable_fields = [
179+
field for field in self.fields.values()
180+
if not field.write_only
181+
]
182+
183+
for field in readable_fields:
184+
try:
185+
186+
if isinstance(field, ModelSerializer) and hasattr(field, field.source + "_id"):
187+
attribute = getattr(instance, field.source + "_id")
188+
if attribute is None:
189+
ret[field.field_name] = None
190+
continue
191+
resource_type = get_resource_type_from_instance(field)
192+
if resource_type:
193+
ret[field.field_name] = OrderedDict([("type", resource_type),
194+
("id", attribute)])
195+
continue
196+
197+
attribute = field.get_attribute(instance)
198+
except SkipField:
199+
continue
200+
201+
# We skip `to_representation` for `None` values so that fields do
202+
# not have to explicitly deal with that case.
203+
#
204+
# For related fields with `use_pk_only_optimization` we need to
205+
# resolve the pk value.
206+
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
207+
if check_for_none is None:
208+
ret[field.field_name] = None
209+
else:
210+
ret[field.field_name] = field.to_representation(attribute)
211+
212+
return ret
213+
173214

174215
class PolymorphicSerializerMetaclass(SerializerMetaclass):
175216
"""

rest_framework_json_api/utils.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -310,12 +310,12 @@ def get_relation_instance(resource_instance, source, serializer):
310310
if serializer_method and hasattr(serializer_method, '__call__'):
311311
relation_instance = serializer_method(resource_instance)
312312
else:
313-
return (False, None)
313+
return False, None
314314

315315
if isinstance(relation_instance, Manager):
316316
relation_instance = relation_instance.all()
317317

318-
return (True, relation_instance)
318+
return True, relation_instance
319319

320320

321321
class Hyperlink(six.text_type):

0 commit comments

Comments
 (0)