|
| 1 | +import inspect |
1 | 2 | from collections import OrderedDict
|
2 | 3 | from functools import singledispatch, wraps
|
3 | 4 |
|
|
25 | 26 | )
|
26 | 27 | from graphene.types.json import JSONString
|
27 | 28 | from graphene.types.scalars import BigInt
|
| 29 | +from graphene.types.resolver import get_default_resolver |
28 | 30 | from graphene.utils.str_converters import to_camel_case
|
29 | 31 | from graphql import GraphQLError
|
30 | 32 |
|
@@ -324,13 +326,49 @@ def wrap_resolve(self, parent_resolver):
|
324 | 326 | resolver = super().wrap_resolve(parent_resolver)
|
325 | 327 |
|
326 | 328 | 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) |
329 | 348 | # In case the resolver is a custom one that overwrites
|
330 | 349 | # 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) |
332 | 369 | return fk_obj
|
333 |
| - return _type.get_node(info, fk_obj.pk) |
| 370 | + else: |
| 371 | + return instance_from_get_node |
334 | 372 |
|
335 | 373 | return custom_resolver
|
336 | 374 |
|
|
0 commit comments