django-bulk-hooks 0.2.74__py3-none-any.whl → 0.2.76__py3-none-any.whl
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.
Potentially problematic release.
This version of django-bulk-hooks might be problematic. Click here for more details.
- django_bulk_hooks/__init__.py +0 -3
- django_bulk_hooks/dispatcher.py +49 -41
- {django_bulk_hooks-0.2.74.dist-info → django_bulk_hooks-0.2.76.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.2.74.dist-info → django_bulk_hooks-0.2.76.dist-info}/RECORD +6 -6
- {django_bulk_hooks-0.2.74.dist-info → django_bulk_hooks-0.2.76.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.74.dist-info → django_bulk_hooks-0.2.76.dist-info}/WHEEL +0 -0
django_bulk_hooks/__init__.py
CHANGED
|
@@ -25,9 +25,6 @@ from django_bulk_hooks.operations import BulkOperationCoordinator
|
|
|
25
25
|
from django_bulk_hooks.operations import ModelAnalyzer
|
|
26
26
|
from django_bulk_hooks.operations import MTIHandler
|
|
27
27
|
|
|
28
|
-
# Add NullHandler to prevent logging messages if the application doesn't configure logging
|
|
29
|
-
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
|
30
|
-
|
|
31
28
|
__all__ = [
|
|
32
29
|
"BulkHookManager",
|
|
33
30
|
"HookClass",
|
django_bulk_hooks/dispatcher.py
CHANGED
|
@@ -191,7 +191,55 @@ class HookDispatcher:
|
|
|
191
191
|
changeset: ChangeSet with all record changes
|
|
192
192
|
event: The hook event (e.g., 'before_create')
|
|
193
193
|
"""
|
|
194
|
-
#
|
|
194
|
+
# Use DI factory to create handler instance EARLY to access method decorators
|
|
195
|
+
from django_bulk_hooks.factory import create_hook_instance
|
|
196
|
+
|
|
197
|
+
handler = create_hook_instance(handler_cls)
|
|
198
|
+
method = getattr(handler, method_name)
|
|
199
|
+
|
|
200
|
+
# PRELOAD @select_related RELATIONSHIPS BEFORE CONDITION EVALUATION
|
|
201
|
+
# This ensures both conditions and hook methods have access to preloaded relationships
|
|
202
|
+
|
|
203
|
+
# Check if method has @select_related decorator
|
|
204
|
+
preload_func = getattr(method, "_select_related_preload", None)
|
|
205
|
+
if preload_func:
|
|
206
|
+
# Preload relationships to prevent N+1 queries in both conditions and hook methods
|
|
207
|
+
try:
|
|
208
|
+
model_cls_override = getattr(handler, "model_cls", None)
|
|
209
|
+
|
|
210
|
+
# Get FK fields being updated to avoid preloading conflicting relationships
|
|
211
|
+
skip_fields = changeset.operation_meta.get("fk_fields_being_updated", set())
|
|
212
|
+
|
|
213
|
+
# Preload for new_records (needed for condition evaluation and hook execution)
|
|
214
|
+
if changeset.new_records:
|
|
215
|
+
preload_func(
|
|
216
|
+
changeset.new_records,
|
|
217
|
+
model_cls=model_cls_override,
|
|
218
|
+
skip_fields=skip_fields,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Also preload for old_records (for conditions that check previous values)
|
|
222
|
+
if changeset.old_records:
|
|
223
|
+
preload_func(
|
|
224
|
+
changeset.old_records,
|
|
225
|
+
model_cls=model_cls_override,
|
|
226
|
+
skip_fields=skip_fields,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
# Mark that relationships have been preloaded to avoid duplicate condition preloading
|
|
230
|
+
changeset.operation_meta['relationships_preloaded'] = True
|
|
231
|
+
logger.debug(f"🔗 @select_related: Preloaded relationships for {handler_cls.__name__}.{method_name}")
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
logger.warning(f"Failed to preload relationships for {handler_cls.__name__}.{method_name}: {e}")
|
|
235
|
+
|
|
236
|
+
# SPECIAL HANDLING: Explicit @select_related support for BEFORE_CREATE hooks
|
|
237
|
+
# (This can stay for additional BEFORE_CREATE-specific logic if needed)
|
|
238
|
+
select_related_fields = getattr(method, "_select_related_fields", None)
|
|
239
|
+
if select_related_fields and event == "before_create" and changeset.new_records:
|
|
240
|
+
self._preload_select_related_for_before_create(changeset, select_related_fields)
|
|
241
|
+
|
|
242
|
+
# NOW condition evaluation is safe - relationships are preloaded
|
|
195
243
|
if condition:
|
|
196
244
|
# Skip per-hook preloading if relationships were already preloaded upfront
|
|
197
245
|
if not changeset.operation_meta.get('relationships_preloaded', False):
|
|
@@ -226,46 +274,6 @@ class HookDispatcher:
|
|
|
226
274
|
# No condition, use full changeset
|
|
227
275
|
filtered_changeset = changeset
|
|
228
276
|
|
|
229
|
-
# Use DI factory to create handler instance
|
|
230
|
-
from django_bulk_hooks.factory import create_hook_instance
|
|
231
|
-
|
|
232
|
-
handler = create_hook_instance(handler_cls)
|
|
233
|
-
method = getattr(handler, method_name)
|
|
234
|
-
|
|
235
|
-
# SPECIAL HANDLING: Explicit @select_related support for BEFORE_CREATE hooks
|
|
236
|
-
# This provides guaranteed bulk preloading to eliminate N+1 queries
|
|
237
|
-
select_related_fields = getattr(method, "_select_related_fields", None)
|
|
238
|
-
if select_related_fields and event == "before_create" and filtered_changeset.new_records:
|
|
239
|
-
self._preload_select_related_for_before_create(filtered_changeset, select_related_fields)
|
|
240
|
-
|
|
241
|
-
# Check if method has @select_related decorator (fallback for other cases)
|
|
242
|
-
preload_func = getattr(method, "_select_related_preload", None)
|
|
243
|
-
if preload_func:
|
|
244
|
-
# Preload relationships to prevent N+1 queries
|
|
245
|
-
try:
|
|
246
|
-
model_cls_override = getattr(handler, "model_cls", None)
|
|
247
|
-
|
|
248
|
-
# Get FK fields being updated to avoid preloading conflicting relationships
|
|
249
|
-
skip_fields = changeset.operation_meta.get("fk_fields_being_updated", set())
|
|
250
|
-
|
|
251
|
-
# Preload for new_records
|
|
252
|
-
if filtered_changeset.new_records:
|
|
253
|
-
preload_func(
|
|
254
|
-
filtered_changeset.new_records,
|
|
255
|
-
model_cls=model_cls_override,
|
|
256
|
-
skip_fields=skip_fields,
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
# Also preload for old_records (for conditions that check previous values)
|
|
260
|
-
if filtered_changeset.old_records:
|
|
261
|
-
preload_func(
|
|
262
|
-
filtered_changeset.old_records,
|
|
263
|
-
model_cls=model_cls_override,
|
|
264
|
-
skip_fields=skip_fields,
|
|
265
|
-
)
|
|
266
|
-
except Exception:
|
|
267
|
-
pass # Preload failed, continue without it
|
|
268
|
-
|
|
269
277
|
# Execute hook with ChangeSet
|
|
270
278
|
#
|
|
271
279
|
# ARCHITECTURE NOTE: Hook Contract
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
django_bulk_hooks/__init__.py,sha256=
|
|
1
|
+
django_bulk_hooks/__init__.py,sha256=ZKjEi9Sj3lRr3hcEfknXAr1UXXwERzUCNgMkNXhW0mk,2119
|
|
2
2
|
django_bulk_hooks/changeset.py,sha256=qnMD3bR2cNh8ZM8J6ASR5ly5Rjx-tPzXBYkqIjKGW98,6568
|
|
3
3
|
django_bulk_hooks/conditions.py,sha256=ar4pGjtxLKmgSIlO4S6aZFKmaBNchLtxMmWpkn4g9RU,8114
|
|
4
4
|
django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
|
|
5
5
|
django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
|
|
6
6
|
django_bulk_hooks/decorators.py,sha256=TdkO4FJyFrVU2zqK6Y_6JjEJ4v3nbKkk7aa22jN10sk,11994
|
|
7
|
-
django_bulk_hooks/dispatcher.py,sha256=
|
|
7
|
+
django_bulk_hooks/dispatcher.py,sha256=mW5gkHFmkw_IdLb3j4QgKfZ9ia4MivuSbxNgp3B_O68,24220
|
|
8
8
|
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
9
9
|
django_bulk_hooks/factory.py,sha256=ezrVM5U023KZqOBbJXb6lYUP-pE7WJmi8Olh2Ew-7RA,18085
|
|
10
10
|
django_bulk_hooks/handler.py,sha256=SRCrMzgolrruTkvMnYBFmXLR-ABiw0JiH3605PEdCZM,4207
|
|
@@ -21,7 +21,7 @@ django_bulk_hooks/operations/mti_plans.py,sha256=HIRJgogHPpm6MV7nZZ-sZhMLUnozpZP
|
|
|
21
21
|
django_bulk_hooks/operations/record_classifier.py,sha256=It85hJC2K-UsEOLbTR-QBdY5UPV-acQIJ91TSGa7pYo,7053
|
|
22
22
|
django_bulk_hooks/queryset.py,sha256=tHt1U2O9Hvozg9sdn5MjzAk_I6wDU-LupuKFTfv1SiQ,7449
|
|
23
23
|
django_bulk_hooks/registry.py,sha256=4HxP1mVK2z4VzvlohbEw2359wM21UJZJYagJJ1komM0,7947
|
|
24
|
-
django_bulk_hooks-0.2.
|
|
25
|
-
django_bulk_hooks-0.2.
|
|
26
|
-
django_bulk_hooks-0.2.
|
|
27
|
-
django_bulk_hooks-0.2.
|
|
24
|
+
django_bulk_hooks-0.2.76.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
25
|
+
django_bulk_hooks-0.2.76.dist-info/METADATA,sha256=j4nb5519S7OtuxUQ2jbrCPMJMVWVJ2Cdvkgrq3tT9zo,10555
|
|
26
|
+
django_bulk_hooks-0.2.76.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
27
|
+
django_bulk_hooks-0.2.76.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|