django-bulk-hooks 0.2.70__py3-none-any.whl → 0.2.71__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/dispatcher.py +58 -2
- django_bulk_hooks/operations/coordinator.py +3 -0
- {django_bulk_hooks-0.2.70.dist-info → django_bulk_hooks-0.2.71.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.2.70.dist-info → django_bulk_hooks-0.2.71.dist-info}/RECORD +6 -6
- {django_bulk_hooks-0.2.70.dist-info → django_bulk_hooks-0.2.71.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.70.dist-info → django_bulk_hooks-0.2.71.dist-info}/WHEEL +0 -0
django_bulk_hooks/dispatcher.py
CHANGED
|
@@ -106,12 +106,68 @@ class HookDispatcher:
|
|
|
106
106
|
if not hooks:
|
|
107
107
|
return
|
|
108
108
|
|
|
109
|
-
#
|
|
110
|
-
|
|
109
|
+
# Create an operation key that includes the changeset model to avoid
|
|
110
|
+
# deduplicating hooks across different operations on the same records
|
|
111
|
+
# This prevents the same hook from executing multiple times for MTI inheritance chains
|
|
112
|
+
# but allows different operations on the same records to execute their hooks
|
|
113
|
+
record_ids = set()
|
|
114
|
+
for change in changeset.changes:
|
|
115
|
+
if change.new_record and change.new_record.pk:
|
|
116
|
+
record_ids.add(change.new_record.pk)
|
|
117
|
+
if change.old_record and change.old_record.pk:
|
|
118
|
+
record_ids.add(change.old_record.pk)
|
|
119
|
+
|
|
120
|
+
# Sort record IDs safely (handle Mock objects and other non-comparable types)
|
|
121
|
+
try:
|
|
122
|
+
sorted_record_ids = tuple(sorted(record_ids, key=lambda x: str(x)))
|
|
123
|
+
except (TypeError, AttributeError):
|
|
124
|
+
# Fallback for non-comparable objects (like Mock objects in tests)
|
|
125
|
+
sorted_record_ids = tuple(record_ids)
|
|
126
|
+
|
|
127
|
+
# Include changeset model and operation details to make the key more specific
|
|
128
|
+
operation_meta = getattr(changeset, 'operation_meta', {}) or {}
|
|
129
|
+
operation_type = getattr(changeset, 'operation_type', 'unknown')
|
|
130
|
+
|
|
131
|
+
# Include update_kwargs if present to distinguish different queryset operations
|
|
132
|
+
update_kwargs = operation_meta.get('update_kwargs', {})
|
|
133
|
+
if update_kwargs:
|
|
134
|
+
try:
|
|
135
|
+
# Convert to a hashable representation
|
|
136
|
+
update_kwargs_key = tuple(sorted((k, str(v)) for k, v in update_kwargs.items()))
|
|
137
|
+
except (TypeError, AttributeError):
|
|
138
|
+
# Fallback if values are not convertible to string
|
|
139
|
+
update_kwargs_key = tuple(sorted(update_kwargs.keys()))
|
|
140
|
+
else:
|
|
141
|
+
update_kwargs_key = ()
|
|
142
|
+
|
|
143
|
+
operation_key = (event, changeset.model_cls.__name__, operation_type, sorted_record_ids, update_kwargs_key)
|
|
144
|
+
|
|
145
|
+
# Track executed hooks to prevent duplicates in MTI inheritance chains
|
|
146
|
+
if not hasattr(self, '_executed_hooks'):
|
|
147
|
+
self._executed_hooks = set()
|
|
148
|
+
|
|
149
|
+
# Filter out hooks that have already been executed for this operation
|
|
150
|
+
unique_hooks = []
|
|
111
151
|
for handler_cls, method_name, condition, priority in hooks:
|
|
152
|
+
hook_key = (handler_cls, method_name, operation_key)
|
|
153
|
+
if hook_key not in self._executed_hooks:
|
|
154
|
+
unique_hooks.append((handler_cls, method_name, condition, priority))
|
|
155
|
+
self._executed_hooks.add(hook_key)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if not unique_hooks:
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
# Execute hooks in priority order
|
|
162
|
+
logger.info(f"🔥 HOOKS: Executing {len(unique_hooks)} hooks for {changeset.model_cls.__name__}.{event}")
|
|
163
|
+
for handler_cls, method_name, condition, priority in unique_hooks:
|
|
112
164
|
logger.info(f" → {handler_cls.__name__}.{method_name} (priority={priority})")
|
|
113
165
|
self._execute_hook(handler_cls, method_name, condition, changeset, event)
|
|
114
166
|
|
|
167
|
+
def _reset_executed_hooks(self):
|
|
168
|
+
"""Reset the executed hooks tracking for a new operation."""
|
|
169
|
+
self._executed_hooks = set()
|
|
170
|
+
|
|
115
171
|
def _execute_hook(self, handler_cls, method_name, condition, changeset, event):
|
|
116
172
|
"""
|
|
117
173
|
Execute a single hook with condition checking.
|
|
@@ -700,6 +700,9 @@ class BulkOperationCoordinator:
|
|
|
700
700
|
if bypass_hooks:
|
|
701
701
|
return operation()
|
|
702
702
|
|
|
703
|
+
# Reset hook execution tracking for this new operation
|
|
704
|
+
self.dispatcher._reset_executed_hooks()
|
|
705
|
+
|
|
703
706
|
# Get all models in inheritance chain
|
|
704
707
|
models_in_chain = self._get_models_in_chain(changeset.model_cls)
|
|
705
708
|
|
|
@@ -4,7 +4,7 @@ django_bulk_hooks/conditions.py,sha256=ar4pGjtxLKmgSIlO4S6aZFKmaBNchLtxMmWpkn4g9
|
|
|
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=QqpaR5l1THsXORZ5kSU9D3ictm7a0Bk47Gy3SbS9Q3A,21584
|
|
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
|
|
@@ -14,14 +14,14 @@ django_bulk_hooks/models.py,sha256=TWN_F-SsLGPx9jrkNT9pmJFR5VsZ0Z_QaVOZOmt7bpw,2
|
|
|
14
14
|
django_bulk_hooks/operations/__init__.py,sha256=BtJYjmRhe_sScivLsniDaZmBkm0ZLvcmzXFKL7QY2Xg,550
|
|
15
15
|
django_bulk_hooks/operations/analyzer.py,sha256=Pz8mc-EL8KDOfLQFYiRuN-r0OmINW3nIBhRJJCma-yo,10360
|
|
16
16
|
django_bulk_hooks/operations/bulk_executor.py,sha256=po8V_2H3ULiE0RYJ-wbaRIz52SKhss81UHwuQjlz3H8,26214
|
|
17
|
-
django_bulk_hooks/operations/coordinator.py,sha256=
|
|
17
|
+
django_bulk_hooks/operations/coordinator.py,sha256=Og8eLVE2svo4r3bfNZpMqy-Dt3q1MKGWz4UXfDUvfgw,38687
|
|
18
18
|
django_bulk_hooks/operations/field_utils.py,sha256=cQ9w4xdk-z3PrMLFvRzVV07Wc0D2qbpSepwoupqwQH8,7888
|
|
19
19
|
django_bulk_hooks/operations/mti_handler.py,sha256=Vmz0C0gtYDvbybmb4cDzIaGglSaQK4DQVkaBK-WuQeE,25855
|
|
20
20
|
django_bulk_hooks/operations/mti_plans.py,sha256=HIRJgogHPpm6MV7nZZ-sZhMLUnozpZPV2SzwQHLRzYc,3667
|
|
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.71.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
25
|
+
django_bulk_hooks-0.2.71.dist-info/METADATA,sha256=pnZqs1yIYf8QwTBfLVggtCMAQ9jQ4O2pMtIakX42wyU,10555
|
|
26
|
+
django_bulk_hooks-0.2.71.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
27
|
+
django_bulk_hooks-0.2.71.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|