Advertisement
Guest User

Untitled

a guest
Apr 26th, 2019
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.92 KB | None | 0 0
  1. import inspect
  2. from collections import OrderedDict
  3. from json import loads, dumps
  4. from pprint import pprint
  5. from typing import Type, Union
  6.  
  7. from django.core.exceptions import FieldError
  8. from django.test import TestCase
  9. from rest_framework.relations import RelatedField, ManyRelatedField
  10. from rest_framework.serializers import ModelSerializer, Serializer, BaseSerializer, ListSerializer
  11. from rest_framework.test import APIRequestFactory
  12. from rest_framework.utils.serializer_helpers import ReturnList
  13.  
  14. #This snippet automatically prefetches all related objects a Serializer needs.
  15. #Example usage in your ViewSets get_queryset method:
  16. def get_queryset():
  17. qs = YOUR_MODEL.objects.all() # Or whatever queryset you want to use
  18. qs = prefetch(self.get_serializer_class(), queryset) # This line prefetches the related model depending on the serializer.
  19. return qs
  20.  
  21. def prefetch(queryset, serializer: Type[ModelSerializer]):
  22. select_related, prefetch_related = _prefetch(serializer)
  23. return queryset.select_related(*select_related).prefetch_related(*prefetch_related)
  24.  
  25. def _prefetch(serializer: Union[Type[BaseSerializer], BaseSerializer], path=None, indentation=0):
  26. """
  27. Returns prefetch_related, select_related
  28. :param serializer:
  29. :return:
  30. """
  31. prepend = f'{path}__' if path is not None else ''
  32. class_name = getattr(serializer, '__name__', serializer.__class__.__name__)
  33.  
  34. print(f'{" " * indentation}LOOKING AT SERIALIZER:', class_name, 'from path: ', prepend)
  35.  
  36. select_related = set()
  37. prefetch_related = set()
  38.  
  39. print()
  40. if inspect.isclass(serializer):
  41. print('serializer is a class')
  42. serializer_instance = serializer()
  43. else:
  44. serializer_instance = serializer
  45.  
  46. try:
  47. fields = getattr(serializer_instance, 'child', serializer_instance).fields.fields.items()
  48. except AttributeError:
  49. # This can happen if there's no further fields, e.g. if we're passed a PrimaryKeyRelatedField
  50. # as the nested representation of a ManyToManyField
  51. return (set(), set())
  52.  
  53. for name, field_instance in fields:
  54. field_type_name = field_instance.__class__.__name__
  55. print(f'{" " * indentation} Field "{name}", type: {field_type_name}, src: "{field_instance.source}"')
  56.  
  57. # We potentially need to recurse deeper
  58. if isinstance(field_instance, (BaseSerializer, RelatedField, ManyRelatedField)):
  59. print(f'{" " * indentation}Found: {field_type_name} ({type(field_instance)}) - recursing deeper')
  60. field_path = f'{prepend}{field_instance.source}'
  61.  
  62. # Fields where the field name *is* the model.
  63. if isinstance(field_instance, RelatedField):
  64. print(f'{" " * indentation} Found related field: ', field_type_name)
  65. select_related.add(f'{prepend}{name}')
  66.  
  67. """
  68. If we have multiple entities, we need to use prefetch_related instead of select_related
  69. We also need to do this for all further calls
  70. """
  71. elif isinstance(field_instance, (ListSerializer, ManyRelatedField)):
  72. print(f'{" " * indentation} Found *:m relation: ', field_type_name)
  73. prefetch_related.add(field_path)
  74.  
  75. # If it's a ManyRelatedField, we can only get the actual underlying field by querying child_relation
  76. nested_field = getattr(field_instance, 'child_relation', field_instance)
  77.  
  78. select, prefetch = _prefetch(nested_field, field_path, indentation + 4)
  79. prefetch_related |= select
  80. prefetch_related |= prefetch
  81. else:
  82. print(f'{" " * indentation} Found *:1 relation: ', field_type_name)
  83. select_related.add(field_path)
  84. select, prefetch = _prefetch(field_instance, field_path, indentation + 4)
  85. select_related |= select
  86. prefetch_related |= prefetch
  87.  
  88. return (select_related, prefetch_related)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement