Skip to content

Commit

Permalink
Avoid query explosion for related fields where I can user pk_only_opt… (
Browse files Browse the repository at this point in the history
#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
  • Loading branch information
santiavenda2 authored and mblayman committed Jan 9, 2018
1 parent b0257c0 commit b4dffb8
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 18 deletions.
5 changes: 2 additions & 3 deletions example/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,11 @@ class AuthorViewSet(ModelViewSet):


class CommentViewSet(ModelViewSet):
queryset = Comment.objects.select_related('author', 'entry')
queryset = Comment.objects.all()
serializer_class = CommentSerializer
prefetch_for_includes = {
'__all__': [],
'author': ['author', 'author__bio', 'author__entries', 'author__type'],
'entry': ['author', 'author__bio', 'author__entries']
'author': ['author__bio', 'author__entries'],
}


Expand Down
2 changes: 1 addition & 1 deletion requirements-development.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ Sphinx
sphinx_rtd_theme
tox
django-debug-toolbar
packaging==16.8
packaging==16.8
5 changes: 4 additions & 1 deletion rest_framework_json_api/relations.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def __init__(self, self_link_view_name=None, related_link_view_name=None, **kwar

def use_pk_only_optimization(self):
# We need the real object to determine its type...
return False
return self.get_resource_type_from_included_serializer() is not None

def conflict(self, key, **kwargs):
"""
Expand Down Expand Up @@ -251,6 +251,9 @@ def __init__(self, polymorphic_serializer, *args, **kwargs):
self.polymorphic_serializer = polymorphic_serializer
super(PolymorphicResourceRelatedField, self).__init__(*args, **kwargs)

def use_pk_only_optimization(self):
return False

def to_internal_value(self, data):
if isinstance(data, six.text_type):
try:
Expand Down
28 changes: 17 additions & 11 deletions rest_framework_json_api/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,12 @@ def extract_relationships(cls, fields, resource, resource_instance):
continue

if isinstance(field, ResourceRelatedField):
resolved, relation_instance = utils.get_relation_instance(
resource_instance, source, field.parent
)
if not resolved:
continue
relation_instance_id = getattr(resource_instance, source + "_id", None)
if not relation_instance_id:
resolved, relation_instance = utils.get_relation_instance(resource_instance,
source, field.parent)
if not resolved:
continue

# special case for ResourceRelatedField
relation_data = {
Expand Down Expand Up @@ -256,18 +257,23 @@ def extract_relationships(cls, fields, resource, resource_instance):
continue

if isinstance(field, Serializer):
resolved, relation_instance = utils.get_relation_instance(
resource_instance, source, field.parent
)
if not resolved:
continue
relation_instance_id = getattr(resource_instance, source + "_id", None)
if not relation_instance_id:
resolved, relation_instance = utils.get_relation_instance(
resource_instance, source, field.parent
)
if not resolved:
continue

if relation_instance is not None:
relation_instance_id = relation_instance.pk

data.update({
field_name: {
'data': (
OrderedDict([
('type', relation_type),
('id', encoding.force_text(relation_instance.pk))
('id', encoding.force_text(relation_instance_id))
]) if resource.get(field_name) else None)
}
})
Expand Down
41 changes: 41 additions & 0 deletions rest_framework_json_api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,47 @@ def get_field_names(self, declared_fields, info):
fields = super(ModelSerializer, self).get_field_names(declared, info)
return list(fields) + list(getattr(self.Meta, 'meta_fields', list()))

def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
readable_fields = [
field for field in self.fields.values()
if not field.write_only
]

for field in readable_fields:
try:

if isinstance(field, ModelSerializer) and hasattr(field, field.source + "_id"):
attribute = getattr(instance, field.source + "_id")
if attribute is None:
ret[field.field_name] = None
continue
resource_type = get_resource_type_from_instance(field)
if resource_type:
ret[field.field_name] = OrderedDict([("type", resource_type),
("id", attribute)])
continue

attribute = field.get_attribute(instance)
except SkipField:
continue

# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)

return ret


class PolymorphicSerializerMetaclass(SerializerMetaclass):
"""
Expand Down
4 changes: 2 additions & 2 deletions rest_framework_json_api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,12 @@ def get_relation_instance(resource_instance, source, serializer):
if serializer_method and hasattr(serializer_method, '__call__'):
relation_instance = serializer_method(resource_instance)
else:
return (False, None)
return False, None

if isinstance(relation_instance, Manager):
relation_instance = relation_instance.all()

return (True, relation_instance)
return True, relation_instance


class Hyperlink(six.text_type):
Expand Down

0 comments on commit b4dffb8

Please sign in to comment.