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

@@ -1,5 +1,7 @@
1
1
  import threading
2
2
  from collections import deque
3
+ from django_bulk_hooks.handler import hook_vars
4
+
3
5
 
4
6
  _hook_context = threading.local()
5
7
 
@@ -11,6 +13,28 @@ def get_hook_queue():
11
13
 
12
14
 
13
15
  class HookContext:
14
- def __init__(self, model_cls, metadata=None):
15
- self.model_cls = model_cls
16
- self.metadata = metadata or {}
16
+ def __init__(self, model):
17
+ self.model = model
18
+
19
+ @property
20
+ def is_executing(self):
21
+ """
22
+ Check if we're currently in a hook execution context.
23
+ Similar to Salesforce's Trigger.isExecuting.
24
+ Use this to prevent infinite recursion in hooks.
25
+ """
26
+ return hasattr(hook_vars, 'event') and hook_vars.event is not None
27
+
28
+ @property
29
+ def current_event(self):
30
+ """
31
+ Get the current hook event being executed.
32
+ """
33
+ return getattr(hook_vars, 'event', None)
34
+
35
+ @property
36
+ def execution_depth(self):
37
+ """
38
+ Get the current execution depth to detect deep recursion.
39
+ """
40
+ return getattr(hook_vars, 'depth', 0)
@@ -1,4 +1,4 @@
1
- from django.db import models, transaction
1
+ from django.db import models
2
2
 
3
3
  from django_bulk_hooks.constants import (
4
4
  AFTER_CREATE,
@@ -22,7 +22,7 @@ class HookModelMixin(models.Model):
22
22
  class Meta:
23
23
  abstract = True
24
24
 
25
- def clean(self):
25
+ def clean(self, bypass_hooks=False):
26
26
  """
27
27
  Override clean() to trigger validation hooks.
28
28
  This ensures that when Django calls clean() (like in admin forms),
@@ -30,6 +30,10 @@ class HookModelMixin(models.Model):
30
30
  """
31
31
  super().clean()
32
32
 
33
+ # If bypass_hooks is True, skip validation hooks
34
+ if bypass_hooks:
35
+ return
36
+
33
37
  # Determine if this is a create or update operation
34
38
  is_create = self.pk is None
35
39
 
@@ -48,7 +52,11 @@ class HookModelMixin(models.Model):
48
52
  ctx = HookContext(self.__class__)
49
53
  run(self.__class__, VALIDATE_CREATE, [self], ctx=ctx)
50
54
 
51
- def save(self, *args, **kwargs):
55
+ def save(self, *args, bypass_hooks=False, **kwargs):
56
+ # If bypass_hooks is True, use base manager to avoid triggering hooks
57
+ if bypass_hooks:
58
+ return self._base_manager.save(self, *args, **kwargs)
59
+
52
60
  is_create = self.pk is None
53
61
 
54
62
  if is_create:
@@ -80,7 +88,11 @@ class HookModelMixin(models.Model):
80
88
 
81
89
  return self
82
90
 
83
- def delete(self, *args, **kwargs):
91
+ def delete(self, *args, bypass_hooks=False, **kwargs):
92
+ # If bypass_hooks is True, use base manager to avoid triggering hooks
93
+ if bypass_hooks:
94
+ return self._base_manager.delete(self, *args, **kwargs)
95
+
84
96
  ctx = HookContext(self.__class__)
85
97
 
86
98
  # Run validation hooks first
@@ -325,6 +325,10 @@ class HookQuerySet(models.QuerySet):
325
325
  parent_objects_map = {}
326
326
 
327
327
  # Step 1: Do O(n) normal inserts into parent tables to get primary keys back
328
+ # Get bypass_hooks from kwargs
329
+ bypass_hooks = kwargs.get('bypass_hooks', False)
330
+ bypass_validation = kwargs.get('bypass_validation', False)
331
+
328
332
  for obj in batch:
329
333
  parent_instances = {}
330
334
  current_parent = None
@@ -332,6 +336,14 @@ class HookQuerySet(models.QuerySet):
332
336
  parent_obj = self._create_parent_instance(
333
337
  obj, model_class, current_parent
334
338
  )
339
+
340
+ # Fire parent hooks if not bypassed
341
+ if not bypass_hooks:
342
+ ctx = HookContext(model_class)
343
+ if not bypass_validation:
344
+ engine.run(model_class, VALIDATE_CREATE, [parent_obj], ctx=ctx)
345
+ engine.run(model_class, BEFORE_CREATE, [parent_obj], ctx=ctx)
346
+
335
347
  # Use Django's base manager to create the object and get PKs back
336
348
  # This bypasses hooks and the MTI exception
337
349
  field_values = {
@@ -340,10 +352,16 @@ class HookQuerySet(models.QuerySet):
340
352
  if hasattr(parent_obj, field.name) and getattr(parent_obj, field.name) is not None
341
353
  }
342
354
  created_obj = model_class._base_manager.using(self.db).create(**field_values)
355
+
343
356
  # Update the parent_obj with the created object's PK
344
357
  parent_obj.pk = created_obj.pk
345
358
  parent_obj._state.adding = False
346
359
  parent_obj._state.db = self.db
360
+
361
+ # Fire AFTER_CREATE hooks for parent
362
+ if not bypass_hooks:
363
+ engine.run(model_class, AFTER_CREATE, [parent_obj], ctx=ctx)
364
+
347
365
  parent_instances[model_class] = parent_obj
348
366
  current_parent = parent_obj
349
367
  parent_objects_map[id(obj)] = parent_instances
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.147
3
+ Version: 0.1.149
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
2
  django_bulk_hooks/conditions.py,sha256=mTvlLcttixbXRkTSNZU5VewkPUavbXRuD2BkJbVWMkw,6041
3
3
  django_bulk_hooks/constants.py,sha256=3x1H1fSUUNo0DZONN7GUVDuySZctTR-jtByBHmAIX5w,303
4
- django_bulk_hooks/context.py,sha256=HVDT73uSzvgrOR6mdXTvsBm3hLOgBU8ant_mB7VlFuM,380
4
+ django_bulk_hooks/context.py,sha256=4IPuOX8TBAYBEMzN0RNHWgE6Giy2ZnR5uRXfd1cpIwk,1051
5
5
  django_bulk_hooks/decorators.py,sha256=tckDcxtOzKCbgvS9QydgeIAWTFDEl-ch3_Q--ruEGdQ,4831
6
6
  django_bulk_hooks/engine.py,sha256=3HbgV12JRYIy9IlygHPxZiHnFXj7EwzLyTuJNQeVIoI,1402
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=r54ct3S6AcqME2OsX-jPF944CEKcoSIW3qiAx_NwUaw,2801
10
- django_bulk_hooks/models.py,sha256=7RG7GrOdHXFjGVPV4FPRZVNMIHHW-hMCi6hn9LH_hVI,3331
10
+ django_bulk_hooks/models.py,sha256=5PBjBoGlHwAE5b4yaZ5kDjt5UtHfQp6pDrwB2XPs2tk,3850
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=Xhb3H5TOSgjuZU0cmAKiHeXJQh-46S7iuHlwtJ3232s,21112
12
+ django_bulk_hooks/queryset.py,sha256=3DOzQ_UJdrngllNskeTrQyxBnFydpS6Vr7Rml9ekrPQ,21914
13
13
  django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
14
- django_bulk_hooks-0.1.147.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.147.dist-info/METADATA,sha256=Gtlkty_v0nDtRzDNNZsFxcQwqetOXgFkzJrlBP4YjHU,6951
16
- django_bulk_hooks-0.1.147.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.147.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.149.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.149.dist-info/METADATA,sha256=rXtNKd2ktak1gSVWdMXlbk6614_LvoZaAvY9GauSHDk,6951
16
+ django_bulk_hooks-0.1.149.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.149.dist-info/RECORD,,