base-api-utils 1.1.100__tar.gz → 1.1.102__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. {base_api_utils-1.1.100/src/base_api_utils.egg-info → base_api_utils-1.1.102}/PKG-INFO +1 -1
  2. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/pyproject.toml +1 -1
  3. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/filters/filter_utils_mixin.py +4 -0
  4. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/__init__.py +1 -0
  5. base_api_utils-1.1.102/src/base_api_utils/utils/expand_queryset_optimization_mixin.py +140 -0
  6. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/views/base_view.py +3 -2
  7. {base_api_utils-1.1.100 → base_api_utils-1.1.102/src/base_api_utils.egg-info}/PKG-INFO +1 -1
  8. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils.egg-info/SOURCES.txt +1 -0
  9. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/LICENSE +0 -0
  10. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/README.md +0 -0
  11. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/setup.cfg +0 -0
  12. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/__init__.py +0 -0
  13. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/commands/__init__.py +0 -0
  14. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/commands/failed_task_management_mixin.py +0 -0
  15. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/domain/__init__.py +0 -0
  16. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/domain/event_message.py +0 -0
  17. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/domain/event_message_factory.py +0 -0
  18. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/domain/events.py +0 -0
  19. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/dtos/__init__.py +0 -0
  20. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/dtos/authz_user.py +0 -0
  21. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/fields/__init__.py +0 -0
  22. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/fields/email_list_field.py +0 -0
  23. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/filters/__init__.py +0 -0
  24. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/filters/base_filter.py +0 -0
  25. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/filters/custom_ordering_filter.py +0 -0
  26. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/__init__.py +0 -0
  27. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/base_event_handler.py +0 -0
  28. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/sponsor_event_handler.py +0 -0
  29. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/sponsorship_addon_event_handler.py +0 -0
  30. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/sponsorship_event_handler.py +0 -0
  31. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/handlers/summit_event_handler.py +0 -0
  32. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/ioc/__init__.py +0 -0
  33. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/ioc/container.py +0 -0
  34. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/models/__init__.py +0 -0
  35. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/models/item_image.py +0 -0
  36. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/__init__.py +0 -0
  37. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/abstract_access_token_service.py +0 -0
  38. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/abstract_oauth2_api_client.py +0 -0
  39. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/access_token_service.py +0 -0
  40. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/check_sponsor_user_permissions.py +0 -0
  41. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/group_required.py +0 -0
  42. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/oauth2_authentication.py +0 -0
  43. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/oauth2_client_factory.py +0 -0
  44. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/oauth2_scope_required.py +0 -0
  45. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/permission_event_handler.py +0 -0
  46. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/shared.py +0 -0
  47. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/security/user_permissions_service.py +0 -0
  48. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/__init__.py +0 -0
  49. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/base_model_serializer.py +0 -0
  50. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/email_list_char_field.py +0 -0
  51. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/nullable_slug_related_field.py +0 -0
  52. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/serializers_registry.py +0 -0
  53. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/task_result_serializer.py +0 -0
  54. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/timestamp_field.py +0 -0
  55. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/v2/__init__.py +0 -0
  56. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/v2/abstract_serializer.py +0 -0
  57. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/v2/base_model_serializer.py +0 -0
  58. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/v2/expands.py +0 -0
  59. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/serializers/v2/query_params.py +0 -0
  60. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/__init__.py +0 -0
  61. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_domain_events_service.py +0 -0
  62. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_event_dispatcher.py +0 -0
  63. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_images_service.py +0 -0
  64. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_sponsor_metadata_service.py +0 -0
  65. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_storage_service.py +0 -0
  66. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/abstract_summit_metadata_service.py +0 -0
  67. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/amqp_service.py +0 -0
  68. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/celery_failed.py +0 -0
  69. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/event_dispatcher.py +0 -0
  70. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/images_service.py +0 -0
  71. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/s3_service.py +0 -0
  72. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/strategies/__init__.py +0 -0
  73. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/strategies/abstract_task_runner_strategy.py +0 -0
  74. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/services/strategies/publish_domain_event_strategy.py +0 -0
  75. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/tests/__init__.py +0 -0
  76. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/tests/test_failed_celery_task_management.py +0 -0
  77. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/urls.py +0 -0
  78. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/async_helpers.py +0 -0
  79. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/circuit_breaker.py +0 -0
  80. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/config.py +0 -0
  81. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/custom_auto_schema.py +0 -0
  82. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/db_connection_helpers.py +0 -0
  83. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/debounce_helpers.py +0 -0
  84. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/enums.py +0 -0
  85. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/env_var_eval.py +0 -0
  86. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/exceptions.py +0 -0
  87. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/external_errors.py +0 -0
  88. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/file_lock.py +0 -0
  89. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/fingerprint_helpers.py +0 -0
  90. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/http_helpers.py +0 -0
  91. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/inject_job.py +0 -0
  92. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/model_helpers.py +0 -0
  93. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/oauth2_custom_schema_base.py +0 -0
  94. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/pagination.py +0 -0
  95. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/rate_utils.py +0 -0
  96. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/read_write_serializer_mixin.py +0 -0
  97. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/serializers.py +0 -0
  98. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/service_utils.py +0 -0
  99. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/string.py +0 -0
  100. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/utils/tests.py +0 -0
  101. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/views/__init__.py +0 -0
  102. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/views/parent_children_crud.py +0 -0
  103. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/views/root_entity_crud.py +0 -0
  104. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils/views/tasks_result_view.py +0 -0
  105. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils.egg-info/dependency_links.txt +0 -0
  106. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils.egg-info/requires.txt +0 -0
  107. {base_api_utils-1.1.100 → base_api_utils-1.1.102}/src/base_api_utils.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: base-api-utils
3
- Version: 1.1.100
3
+ Version: 1.1.102
4
4
  Summary: Django Rest Framework micro services common utilities
5
5
  Author-email: Román Gutierrez <roman@tipit.net>, Sebastian Marcet <sebastian@tipit.net>
6
6
  Project-URL: Homepage, https://github.com/fntechgit/base-api-utils
@@ -7,7 +7,7 @@ where = ["src"]
7
7
 
8
8
  [project]
9
9
  name = "base-api-utils"
10
- version = "1.1.100"
10
+ version = "1.1.102"
11
11
  dependencies = [
12
12
  "boto3",
13
13
  "celery",
@@ -52,3 +52,7 @@ class FilterUtilsMixin:
52
52
  except ValueError:
53
53
  raise ValidationError("Invalid filter format. Expected 'key=value'.")
54
54
  return field_name, operator, value
55
+
56
+ def filter_isnull(self, queryset, field_name, value):
57
+ is_null = str(value).lower() == 'true'
58
+ return Q(**{f"{field_name}__isnull": is_null})
@@ -6,6 +6,7 @@ from .debounce_helpers import debounce_task
6
6
  from .enums import EnumChoices
7
7
  from .env_var_eval import env_bool
8
8
  from .exceptions import S3Exception, custom_exception_handler
9
+ from .expand_queryset_optimization_mixin import ExpandQuerysetOptimizationMixin
9
10
  from .external_errors import NamedCircuitBreakerError, handle_external_service_errors
10
11
  from .file_lock import FileLock
11
12
  from .fingerprint_helpers import (
@@ -0,0 +1,140 @@
1
+ from typing import Any, Iterable, List, Set, Tuple
2
+
3
+ from rest_framework.response import Response
4
+
5
+ from src.base_api_utils.serializers.v2.query_params import (
6
+ parse_request_trees,
7
+ should_expand,
8
+ subtree,
9
+ )
10
+
11
+ class ExpandQuerysetOptimizationMixin:
12
+ """Auto-optimizes queryset from serializer.expand_mappings (recursive), plus runs bulk hooks for computed relations."""
13
+
14
+ def _parse_trees(self):
15
+ return parse_request_trees(self.request)
16
+
17
+ def _include_field(self, fields_tree: dict | None, attr: str) -> bool:
18
+ if fields_tree is None:
19
+ return True
20
+ return attr in fields_tree
21
+
22
+ def _collect_orm_recursive(
23
+ self,
24
+ serializer_cls: type,
25
+ *,
26
+ expand_tree: dict,
27
+ fields_tree: dict | None,
28
+ relations_tree: dict | None,
29
+ prefix: str = "",
30
+ ) -> Tuple[Set[str], Set[str], List[Tuple[str, dict, dict | None, dict | None]]]:
31
+ select_paths: Set[str] = set()
32
+ prefetch_paths: Set[str] = set()
33
+ bulk_hooks: List[Tuple[str, dict, dict | None, dict | None]] = []
34
+
35
+ mappings = getattr(serializer_cls, "expand_mappings", {}) or {}
36
+ for attr, spec in mappings.items():
37
+ if not spec:
38
+ continue
39
+
40
+ orm = spec.get("orm") or {}
41
+ verify = bool(spec.get("verify_relation", False))
42
+
43
+ is_included = self._include_field(fields_tree, attr)
44
+ is_expanded = should_expand(expand_tree, relations_tree, attr, verify)
45
+
46
+ for sr in orm.get("select_related", []) or []:
47
+ if is_expanded:
48
+ select_paths.add(f"{prefix}{sr}" if prefix else sr)
49
+
50
+ for pr in orm.get("prefetch_related", []) or []:
51
+ if is_included:
52
+ prefetch_paths.add(f"{prefix}{pr}" if prefix else pr)
53
+
54
+ hook = orm.get("bulk_prefetch")
55
+ if hook and is_expanded:
56
+ bulk_hooks.append(
57
+ (
58
+ hook,
59
+ subtree(expand_tree, attr) or {},
60
+ subtree(fields_tree, attr),
61
+ subtree(relations_tree, attr),
62
+ )
63
+ )
64
+
65
+ # recurse only if expanded
66
+ if is_expanded:
67
+ child_cls = spec.get("serializer")
68
+ if child_cls:
69
+ child_expand = subtree(expand_tree, attr) or {}
70
+ child_fields = subtree(fields_tree, attr)
71
+ child_relations = subtree(relations_tree, attr)
72
+
73
+ join_seg = spec.get("source") or attr
74
+ if orm.get("nested_prefix") is not None:
75
+ join_seg = orm.get("nested_prefix")
76
+
77
+ # If this relation is computed/bulk, we can't build ORM paths on base qs,
78
+ # but recursion still matters for hooks (they get subtrees).
79
+ if not orm.get("bulk_prefetch"):
80
+ child_prefix = (
81
+ f"{prefix}{join_seg}__" if prefix else f"{join_seg}__"
82
+ )
83
+ c_sel, c_pre, c_hooks = self._collect_orm_recursive(
84
+ child_cls,
85
+ expand_tree=child_expand,
86
+ fields_tree=child_fields,
87
+ relations_tree=child_relations,
88
+ prefix=child_prefix,
89
+ )
90
+ select_paths |= c_sel
91
+ prefetch_paths |= c_pre
92
+ bulk_hooks += c_hooks
93
+
94
+ return select_paths, prefetch_paths, bulk_hooks
95
+
96
+ def get_queryset(self):
97
+ qs = super().get_queryset()
98
+ expand_tree, fields_tree, relations_tree = self._parse_trees()
99
+ serializer_cls = self.get_serializer_class()
100
+
101
+ select_paths, prefetch_paths, _ = self._collect_orm_recursive(
102
+ serializer_cls,
103
+ expand_tree=expand_tree,
104
+ fields_tree=fields_tree,
105
+ relations_tree=relations_tree,
106
+ prefix="",
107
+ )
108
+
109
+ if select_paths:
110
+ qs = qs.select_related(*sorted(select_paths))
111
+ if prefetch_paths:
112
+ qs = qs.prefetch_related(*sorted(prefetch_paths))
113
+ return qs
114
+
115
+ def _run_bulk_hooks(self, objects: Iterable[Any]):
116
+ expand_tree, fields_tree, relations_tree = self._parse_trees()
117
+ serializer_cls = self.get_serializer_class()
118
+ _, _, hooks = self._collect_orm_recursive(
119
+ serializer_cls,
120
+ expand_tree=expand_tree,
121
+ fields_tree=fields_tree,
122
+ relations_tree=relations_tree,
123
+ prefix="",
124
+ )
125
+ for hook_name, ex_sub, fi_sub, rel_sub in hooks:
126
+ fn = getattr(self, f"bulk_prefetch__{hook_name}", None)
127
+ if fn:
128
+ fn(objects, ex_sub, fi_sub, rel_sub)
129
+
130
+ def list(self, request, *args, **kwargs):
131
+ queryset = self.filter_queryset(self.get_queryset())
132
+ page = self.paginate_queryset(queryset)
133
+ items = page if page is not None else list(queryset)
134
+
135
+ self._run_bulk_hooks(items)
136
+
137
+ serializer = self.get_serializer(items, many=True)
138
+ if page is not None:
139
+ return self.get_paginated_response(serializer.data)
140
+ return Response(serializer.data)
@@ -1,8 +1,9 @@
1
1
  from rest_framework.viewsets import ModelViewSet
2
- from ..utils import config, ReadWriteSerializerMixin
3
2
 
3
+ from ..utils import config, ReadWriteSerializerMixin, ExpandQuerysetOptimizationMixin
4
4
 
5
- class BaseView(ReadWriteSerializerMixin, ModelViewSet):
5
+
6
+ class BaseView(ReadWriteSerializerMixin, ExpandQuerysetOptimizationMixin, ModelViewSet):
6
7
  ordering_fields = {}
7
8
 
8
9
  def get_queryset(self):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: base-api-utils
3
- Version: 1.1.100
3
+ Version: 1.1.102
4
4
  Summary: Django Rest Framework micro services common utilities
5
5
  Author-email: Román Gutierrez <roman@tipit.net>, Sebastian Marcet <sebastian@tipit.net>
6
6
  Project-URL: Homepage, https://github.com/fntechgit/base-api-utils
@@ -83,6 +83,7 @@ src/base_api_utils/utils/debounce_helpers.py
83
83
  src/base_api_utils/utils/enums.py
84
84
  src/base_api_utils/utils/env_var_eval.py
85
85
  src/base_api_utils/utils/exceptions.py
86
+ src/base_api_utils/utils/expand_queryset_optimization_mixin.py
86
87
  src/base_api_utils/utils/external_errors.py
87
88
  src/base_api_utils/utils/file_lock.py
88
89
  src/base_api_utils/utils/fingerprint_helpers.py