django-bulk-hooks 0.1.216__py3-none-any.whl → 0.1.218__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.

@@ -77,13 +77,8 @@ class HasChanged(HookCondition):
77
77
  previous = resolve_dotted_attr(original_instance, self.field)
78
78
 
79
79
  # Add more detailed debugging
80
- print(f"DEBUG: Field '{self.field}' - current value: {current} (type: {type(current)})")
81
- print(f"DEBUG: Field '{self.field}' - previous value: {previous} (type: {type(previous)})")
82
- print(f"DEBUG: Values are equal: {current == previous}")
83
-
84
80
  result = (current != previous) == self.has_changed
85
- print(f"DEBUG: HasChanged result: current={current}, previous={previous}, has_changed={self.has_changed}, result={result}")
86
-
81
+ print(f"DEBUG: HasChanged {self.field} result={result}")
87
82
  return result
88
83
 
89
84
 
@@ -23,17 +23,11 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
23
23
  import traceback
24
24
 
25
25
  stack = traceback.format_stack()
26
- print(
27
- f"DEBUG: engine.run called for {model_cls.__name__}.{event} with {len(new_records)} records"
28
- )
29
- print(f"DEBUG: Call stack (last 3 frames):")
30
- for line in stack[-4:-1]: # Show last 3 frames before this one
31
- print(f" {line.strip()}")
32
- print(f"DEBUG: Total hooks found: {len(hooks)}")
26
+ print(f"DEBUG: engine.run {model_cls.__name__}.{event} {len(new_records)} records")
33
27
 
34
28
  # Check if we're in a bypass context
35
29
  if ctx and hasattr(ctx, 'bypass_hooks') and ctx.bypass_hooks:
36
- print(f"DEBUG: Context has bypass_hooks=True, skipping hook execution")
30
+ print(f"DEBUG: engine.run bypassed")
37
31
  return
38
32
 
39
33
  # For BEFORE_* events, run model.clean() first for validation
@@ -47,7 +41,7 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
47
41
 
48
42
  # Process hooks
49
43
  for handler_cls, method_name, condition, priority in hooks:
50
- print(f"DEBUG: Processing hook {handler_cls.__name__}.{method_name} with condition: {condition}")
44
+ print(f"DEBUG: Processing {handler_cls.__name__}.{method_name}")
51
45
  handler_instance = handler_cls()
52
46
  func = getattr(handler_instance, method_name)
53
47
 
@@ -60,20 +54,16 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
60
54
  strict=True,
61
55
  ):
62
56
  if not condition:
63
- print(f"DEBUG: No condition, adding record {new.pk if hasattr(new, 'pk') else 'No PK'}")
64
57
  to_process_new.append(new)
65
58
  to_process_old.append(original)
66
59
  else:
67
60
  condition_result = condition.check(new, original)
68
- print(f"DEBUG: Condition {condition.__class__.__name__} check result: {condition_result} for record {new.pk if hasattr(new, 'pk') else 'No PK'}")
69
61
  if condition_result:
70
62
  to_process_new.append(new)
71
63
  to_process_old.append(original)
72
64
 
73
65
  if to_process_new:
74
- print(
75
- f"DEBUG: Executing hook {handler_cls.__name__}.{method_name} for {len(to_process_new)} records"
76
- )
66
+ print(f"DEBUG: Executing {handler_cls.__name__}.{method_name} for {len(to_process_new)} records")
77
67
  try:
78
68
  func(
79
69
  new_records=to_process_new,
@@ -82,5 +72,3 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
82
72
  except Exception as e:
83
73
  print(f"DEBUG: Hook execution failed: {e}")
84
74
  raise
85
- else:
86
- print(f"DEBUG: No records to process for hook {handler_cls.__name__}.{method_name}")
@@ -78,12 +78,14 @@ class HookQuerySetMixin:
78
78
 
79
79
  # If we're in a bulk operation context, skip hooks to prevent double execution
80
80
  if current_bypass_hooks:
81
- print(f"DEBUG: update method skipping hooks due to bulk operation context")
81
+ print(f"DEBUG: update skipping hooks (bulk context)")
82
82
  ctx = HookContext(model_cls, bypass_hooks=True)
83
83
  else:
84
- print(f"DEBUG: update method running hooks (standalone update)")
84
+ print(f"DEBUG: update running hooks (standalone)")
85
85
  ctx = HookContext(model_cls, bypass_hooks=False)
86
- # Run BEFORE_UPDATE hooks only for standalone updates
86
+ # Run validation hooks first
87
+ engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
88
+ # Then run BEFORE_UPDATE hooks
87
89
  engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
88
90
 
89
91
  # Use Django's built-in update logic directly
@@ -113,10 +115,10 @@ class HookQuerySetMixin:
113
115
 
114
116
  # Run AFTER_UPDATE hooks only for standalone updates
115
117
  if not current_bypass_hooks:
116
- print(f"DEBUG: update method running AFTER_UPDATE hooks")
118
+ print(f"DEBUG: update running AFTER_UPDATE")
117
119
  engine.run(model_cls, AFTER_UPDATE, instances, originals, ctx=ctx)
118
120
  else:
119
- print(f"DEBUG: update method skipping AFTER_UPDATE hooks due to bulk operation context")
121
+ print(f"DEBUG: update skipping AFTER_UPDATE (bulk context)")
120
122
 
121
123
  return update_count
122
124
 
@@ -181,7 +183,7 @@ class HookQuerySetMixin:
181
183
  engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
182
184
  else:
183
185
  ctx = HookContext(model_cls, bypass_hooks=True) # Pass bypass_hooks
184
- print(f"DEBUG: Set thread-local bypass_hooks=True for nested calls in bulk_create")
186
+ print(f"DEBUG: bulk_create bypassed hooks")
185
187
 
186
188
  # For MTI models, we need to handle them specially
187
189
  if is_mti:
@@ -236,9 +238,7 @@ class HookQuerySetMixin:
236
238
  f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
237
239
  )
238
240
 
239
- print(f"DEBUG: bulk_update called with bypass_hooks={bypass_hooks} (type: {type(bypass_hooks)}), bypass_validation={bypass_validation}")
240
- print(f"DEBUG: bulk_update for model {model_cls.__name__} with {len(objs)} objects")
241
- print(f"DEBUG: kwargs: {kwargs}")
241
+ print(f"DEBUG: bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}")
242
242
 
243
243
  # Check for MTI
244
244
  is_mti = False
@@ -248,35 +248,13 @@ class HookQuerySetMixin:
248
248
  break
249
249
 
250
250
  if not bypass_hooks:
251
- print(f"DEBUG: bulk_update running hooks for {len(objs)} objects")
252
- original_map = {
253
- obj.pk: obj
254
- for obj in model_cls._base_manager.filter(
255
- pk__in=[obj.pk for obj in objs]
256
- )
257
- }
258
- originals = [original_map.get(obj.pk) for obj in objs]
259
-
251
+ print(f"DEBUG: bulk_update setting bypass_hooks=False (hooks will run in update())")
260
252
  ctx = HookContext(model_cls, bypass_hooks=False)
261
-
262
- # Run validation hooks first
263
- if not bypass_validation:
264
- engine.run(model_cls, VALIDATE_UPDATE, objs, originals, ctx=ctx)
265
-
266
- # Then run business logic hooks
267
- engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
268
-
269
- # Detect modified fields during hooks
270
- modified_fields = self._detect_modified_fields(objs, originals)
271
- if modified_fields:
272
- fields_set = set(fields)
273
- fields_set.update(modified_fields)
274
- fields = list(fields_set)
253
+ originals = [None] * len(objs) # Placeholder for after_update call
275
254
  else:
276
- print(f"DEBUG: bulk_update skipping hooks and setting bulk context to prevent double execution")
255
+ print(f"DEBUG: bulk_update setting bypass_hooks=True (no hooks)")
277
256
  ctx = HookContext(model_cls, bypass_hooks=True)
278
- print(f"DEBUG: Set thread-local bypass_hooks=True to prevent nested update() calls from running hooks")
279
- originals = [None] * len(objs) # Ensure originals is defined for after_update call
257
+ originals = [None] * len(objs) # Ensure originals is defined for after_update call
280
258
 
281
259
  # Handle auto_now fields like Django's update_or_create does
282
260
  fields_set = set(fields)
@@ -301,22 +279,16 @@ class HookQuerySetMixin:
301
279
  for k, v in kwargs.items()
302
280
  if k not in ["bypass_hooks", "bypass_validation"]
303
281
  }
304
- print(f"DEBUG: Calling Django's bulk_update with kwargs: {django_kwargs}")
305
- # Call Django's bulk_update with hook suspension to prevent double execution
306
- # Django's bulk_update internally calls .update() which would trigger our hooks again
307
- print(f"DEBUG: About to call Django's bulk_update")
282
+ print(f"DEBUG: Calling Django bulk_update")
308
283
  result = super().bulk_update(objs, fields, **django_kwargs)
309
- print(f"DEBUG: Django's bulk_update completed, result: {result}")
284
+ print(f"DEBUG: Django bulk_update done: {result}")
310
285
 
286
+ # Note: We don't run AFTER_UPDATE hooks here to prevent double execution
287
+ # The update() method will handle all hook execution based on thread-local state
311
288
  if not bypass_hooks:
312
- print(
313
- f"DEBUG: bypass_hooks=False, running AFTER_UPDATE hooks for {len(objs)} objects"
314
- )
315
- engine.run(model_cls, AFTER_UPDATE, objs, originals, ctx=ctx)
289
+ print(f"DEBUG: bulk_update skipping AFTER_UPDATE (update() will handle)")
316
290
  else:
317
- print(
318
- f"DEBUG: bypass_hooks=True, skipping AFTER_UPDATE hooks for {len(objs)} objects"
319
- )
291
+ print(f"DEBUG: bulk_update bypassed hooks")
320
292
 
321
293
  return result
322
294
 
@@ -14,21 +14,13 @@ def register_hook(
14
14
  hooks.append((handler_cls, method_name, condition, priority))
15
15
  # keep sorted by priority
16
16
  hooks.sort(key=lambda x: x[3])
17
- print(f"DEBUG: Registered hook {handler_cls.__name__}.{method_name} for {model.__name__}.{event} with condition: {condition}")
18
- print(f"DEBUG: Model class: {model} (id: {id(model)})")
19
- print(f"DEBUG: Total hooks for {model.__name__}.{event}: {len(hooks)}")
17
+ print(f"DEBUG: Registered {handler_cls.__name__}.{method_name} for {model.__name__}.{event}")
20
18
 
21
19
 
22
20
  def get_hooks(model, event):
23
21
  key = (model, event)
24
22
  hooks = _hooks.get(key, [])
25
- print(f"DEBUG: get_hooks called for {model.__name__}.{event}, found {len(hooks)} hooks")
26
- print(f"DEBUG: Hook key: {key}")
27
- print(f"DEBUG: Model class: {model} (id: {id(model)})")
28
- print(f"DEBUG: All registered hook keys: {list(_hooks.keys())}")
29
- for hook in hooks:
30
- handler_cls, method_name, condition, priority = hook
31
- print(f"DEBUG: Hook: {handler_cls.__name__}.{method_name} with condition: {condition}")
23
+ print(f"DEBUG: get_hooks {model.__name__}.{event} found {len(hooks)} hooks")
32
24
  return hooks
33
25
 
34
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.216
3
+ Version: 0.1.218
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  License: MIT
6
6
  Keywords: django,bulk,hooks
@@ -1,17 +1,17 @@
1
1
  django_bulk_hooks/__init__.py,sha256=uUgpnb9AWjIAcWNpCMqBcOewSnpJjJYH6cjPbQkzoNU,140
2
- django_bulk_hooks/conditions.py,sha256=0NEAtiIxS9DI4DcqvqGPaCJJR-5ldHqY9uQ9VuM2YYc,6865
2
+ django_bulk_hooks/conditions.py,sha256=xHazd2hmSddQ3QcButFHjD4rG01tzD4hQMu8uoe7MRY,6513
3
3
  django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
4
4
  django_bulk_hooks/context.py,sha256=_NbGWTq9s66g0vbFIaqN4GlIHWQmFg3EQ44qY8YvvEg,1537
5
5
  django_bulk_hooks/decorators.py,sha256=WD7Jn7QAvY8F4wOsYlIpjoM9-FdHXSKB7hH9ot-lkYQ,4896
6
- django_bulk_hooks/engine.py,sha256=pZGN3-O7SRZeMq69DVjtAAU7CQzcm8sj8GZyTUctBdc,3140
6
+ django_bulk_hooks/engine.py,sha256=dqCiujwpZAYDAmy08wUsGf4TvcppV7JbA9gYxCBdbz8,2412
7
7
  django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
8
8
  django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,4854
9
9
  django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
10
  django_bulk_hooks/models.py,sha256=TA2dBIA1nJBiYt6joefWkpFIQZWysF9kZlkBYvEe59c,4358
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=Lo0_BIBRX_ADDyXUCeCQ-jw4Kr3XaThrOQTaERMEe-s,34033
13
- django_bulk_hooks/registry.py,sha256=fbr6T9MidCs0oV6LhocLIVo3TGs2EazGTVqapoUbm2U,1436
14
- django_bulk_hooks-0.1.216.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.216.dist-info/METADATA,sha256=AmuIXkrR8uVi8e88h74tUym5nS0WHkO0U_RJQu-uBwo,9061
16
- django_bulk_hooks-0.1.216.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.216.dist-info/RECORD,,
12
+ django_bulk_hooks/queryset.py,sha256=bkhhhL7eA1UWL5fxErXEnyqjzhq8xNXT6m79XArly_I,32602
13
+ django_bulk_hooks/registry.py,sha256=XnL6yH6xKgD931KKsFMmemWw9wMu8gj5Cfa7Kn4dcsI,901
14
+ django_bulk_hooks-0.1.218.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.218.dist-info/METADATA,sha256=pl4jKdpEGQ2EZoRLA-iFK0ZlN95kiILxPQ_sQRH30q4,9061
16
+ django_bulk_hooks-0.1.218.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.218.dist-info/RECORD,,