Skip to content

Commit c56822a

Browse files
committed
fix(converter.py): performance fix in convert_field_to_djangomodel.dynamic_type.CustomField. Get instance from the database only one time.
1 parent d18cab8 commit c56822a

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

graphene_django/converter.py

+42-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
from collections import OrderedDict
23
from functools import singledispatch, wraps
34

@@ -25,6 +26,7 @@
2526
)
2627
from graphene.types.json import JSONString
2728
from graphene.types.scalars import BigInt
29+
from graphene.types.resolver import get_default_resolver
2830
from graphene.utils.str_converters import to_camel_case
2931
from graphql import GraphQLError
3032

@@ -324,13 +326,49 @@ def wrap_resolve(self, parent_resolver):
324326
resolver = super().wrap_resolve(parent_resolver)
325327

326328
def custom_resolver(root, info, **args):
327-
fk_obj = resolver(root, info, **args)
328-
if not isinstance(fk_obj, model):
329+
# Note: this function is used to resolve FK or 1:1 fields
330+
# it does not differentiate between custom-resolved fields
331+
# and default resolved fields.
332+
333+
# because this is a django foreign key or one-to-one field, the pk for
334+
# this node can be accessed from the root node.
335+
# ex: article.reporter_id
336+
337+
# or get the field name from the model itself to be more precise
338+
fk_object_key = info.field_name + "_id"
339+
if hasattr(root, fk_object_key):
340+
fk_object_id = getattr(root, fk_object_key)
341+
else:
342+
return None
343+
344+
is_resolver_awaitable = inspect.iscoroutinefunction(resolver)
345+
346+
if is_resolver_awaitable:
347+
fk_obj = resolver(root, info, **args)
329348
# In case the resolver is a custom one that overwrites
330349
# the default Django resolver
331-
# This happens, for example, when using custom awaitable resolvers.
350+
# In this case, it is a custom awaitable resolver.
351+
return fk_obj
352+
353+
instance_from_get_node = _type.get_node(info, fk_object_id)
354+
355+
if not instance_from_get_node:
356+
# no instance to return
357+
return
358+
elif resolver is not get_default_resolver():
359+
# Default resolver is overriden
360+
# For optimization, add the instance to the resolver
361+
setattr(root, info.field_name, instance_from_get_node)
362+
# Explanation: previously, `_type.get_node` is called which results in
363+
# -at least- one hit to the database.
364+
# But, if we did not pass the instance to the root, calling the resolver will result in
365+
# another call to get the instance which results in at least two database queries to resolve
366+
# this node only.
367+
368+
fk_obj = resolver(root, info, **args)
332369
return fk_obj
333-
return _type.get_node(info, fk_obj.pk)
370+
else:
371+
return instance_from_get_node
334372

335373
return custom_resolver
336374

0 commit comments

Comments
 (0)