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

@@ -10,6 +10,16 @@ def get_hook_queue():
10
10
  return _hook_context.queue
11
11
 
12
12
 
13
+ def is_in_bulk_operation():
14
+ """Check if we're currently in a bulk operation to prevent recursion."""
15
+ return getattr(_hook_context, "in_bulk_operation", False)
16
+
17
+
18
+ def set_bulk_operation_flag(value):
19
+ """Set the bulk operation flag to prevent recursion."""
20
+ _hook_context.in_bulk_operation = value
21
+
22
+
13
23
  class HookContext:
14
24
  def __init__(self, model_cls, metadata=None):
15
25
  self.model_cls = model_cls
@@ -1,4 +1,5 @@
1
1
  from django.db import models, transaction
2
+
2
3
  from django_bulk_hooks import engine
3
4
  from django_bulk_hooks.constants import (
4
5
  AFTER_CREATE,
@@ -8,10 +9,10 @@ from django_bulk_hooks.constants import (
8
9
  BEFORE_DELETE,
9
10
  BEFORE_UPDATE,
10
11
  VALIDATE_CREATE,
11
- VALIDATE_UPDATE,
12
12
  VALIDATE_DELETE,
13
+ VALIDATE_UPDATE,
13
14
  )
14
- from django_bulk_hooks.context import HookContext
15
+ from django_bulk_hooks.context import HookContext, is_in_bulk_operation, set_bulk_operation_flag
15
16
  from django_bulk_hooks.queryset import HookQuerySet
16
17
 
17
18
 
@@ -22,7 +23,9 @@ class BulkHookManager(models.Manager):
22
23
  return HookQuerySet(self.model, using=self._db)
23
24
 
24
25
  @transaction.atomic
25
- def bulk_update(self, objs, fields, bypass_hooks=False, bypass_validation=False, **kwargs):
26
+ def bulk_update(
27
+ self, objs, fields, bypass_hooks=False, bypass_validation=False, **kwargs
28
+ ):
26
29
  if not objs:
27
30
  return []
28
31
 
@@ -34,13 +37,27 @@ class BulkHookManager(models.Manager):
34
37
  )
35
38
 
36
39
  if not bypass_hooks:
37
- originals = list(model_cls.objects.filter(pk__in=[obj.pk for obj in objs]))
40
+ # Check if we're already in a hook context (recursive call)
41
+ # If so, refetch the current database state like Salesforce does
42
+ import threading
43
+
44
+ if hasattr(threading.current_thread(), "_hook_context"):
45
+ # We're in a recursive call - refetch current DB state
46
+ originals = list(
47
+ model_cls.objects.filter(pk__in=[obj.pk for obj in objs])
48
+ )
49
+ else:
50
+ # First call - use the passed originals
51
+ originals = list(
52
+ model_cls.objects.filter(pk__in=[obj.pk for obj in objs])
53
+ )
54
+
38
55
  ctx = HookContext(model_cls)
39
-
56
+
40
57
  # Run validation hooks first
41
58
  if not bypass_validation:
42
59
  engine.run(model_cls, VALIDATE_UPDATE, objs, originals, ctx=ctx)
43
-
60
+
44
61
  # Then run business logic hooks
45
62
  engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
46
63
 
@@ -118,11 +135,11 @@ class BulkHookManager(models.Manager):
118
135
 
119
136
  if not bypass_hooks:
120
137
  ctx = HookContext(model_cls)
121
-
138
+
122
139
  # Run validation hooks first
123
140
  if not bypass_validation:
124
141
  engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
125
-
142
+
126
143
  # Then run business logic hooks
127
144
  engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
128
145
 
@@ -136,7 +153,9 @@ class BulkHookManager(models.Manager):
136
153
  return result
137
154
 
138
155
  @transaction.atomic
139
- def bulk_delete(self, objs, batch_size=None, bypass_hooks=False, bypass_validation=False):
156
+ def bulk_delete(
157
+ self, objs, batch_size=None, bypass_hooks=False, bypass_validation=False
158
+ ):
140
159
  if not objs:
141
160
  return []
142
161
 
@@ -153,12 +172,19 @@ class BulkHookManager(models.Manager):
153
172
  # Run validation hooks first
154
173
  if not bypass_validation:
155
174
  engine.run(model_cls, VALIDATE_DELETE, objs, ctx=ctx)
156
-
175
+
157
176
  # Then run business logic hooks
158
177
  engine.run(model_cls, BEFORE_DELETE, objs, ctx=ctx)
159
178
 
160
179
  pks = [obj.pk for obj in objs if obj.pk is not None]
161
- model_cls.objects.filter(pk__in=pks).delete()
180
+
181
+ # Set flag to prevent recursion during the actual deletion
182
+ set_bulk_operation_flag(True)
183
+ try:
184
+ # Use the custom manager - hooks won't fire again due to the flag
185
+ model_cls.objects.filter(pk__in=pks).delete()
186
+ finally:
187
+ set_bulk_operation_flag(False)
162
188
 
163
189
  if not bypass_hooks:
164
190
  engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
@@ -1,4 +1,5 @@
1
1
  from django.db import models, transaction
2
+ from django_bulk_hooks.context import is_in_bulk_operation
2
3
 
3
4
 
4
5
  class HookQuerySet(models.QuerySet):
@@ -7,7 +8,13 @@ class HookQuerySet(models.QuerySet):
7
8
  objs = list(self)
8
9
  if not objs:
9
10
  return 0
10
- return self.model.objects.bulk_delete(objs)
11
+
12
+ # If we're already in a bulk operation, use base manager to prevent recursion
13
+ if is_in_bulk_operation():
14
+ return self.model._base_manager.bulk_delete(objs)
15
+ else:
16
+ # Normal case - use custom manager to ensure hooks fire
17
+ return self.model.objects.bulk_delete(objs)
11
18
 
12
19
  @transaction.atomic
13
20
  def update(self, **kwargs):
@@ -35,8 +42,14 @@ class HookQuerySet(models.QuerySet):
35
42
  engine.run(model_cls, "before_update", instances, originals, ctx=ctx)
36
43
 
37
44
  # Use Django's built-in update logic directly
38
- queryset = self.model._base_manager.filter(pk__in=pks)
39
- update_count = queryset.update(**kwargs)
45
+ # Set flag to prevent recursion during the actual update
46
+ from django_bulk_hooks.context import set_bulk_operation_flag
47
+ set_bulk_operation_flag(True)
48
+ try:
49
+ queryset = self.model.objects.filter(pk__in=pks)
50
+ update_count = queryset.update(**kwargs)
51
+ finally:
52
+ set_bulk_operation_flag(False)
40
53
 
41
54
  # Run AFTER_UPDATE hooks
42
55
  engine.run(model_cls, "after_update", instances, originals, ctx=ctx)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.71
3
+ Version: 0.1.73
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=4fUfw6eMDu2V4h9EFY7uy8uDcIL0v0Fk2YVSn6V1xMI,701
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
- django_bulk_hooks/manager.py,sha256=8ifRsHFJZXViTHdKbVWFd35MF5xHvGqu9UttgsLrkVU,6911
9
+ django_bulk_hooks/manager.py,sha256=PYPq0incwM16tULpCfbR93GrGHcPtGyj4w6s0w1HCn0,7788
10
10
  django_bulk_hooks/models.py,sha256=7RG7GrOdHXFjGVPV4FPRZVNMIHHW-hMCi6hn9LH_hVI,3331
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=nmVZ6cqkveSiBz_L4cRT-3u-mkcjkGQNJLhza8SSdKc,1398
12
+ django_bulk_hooks/queryset.py,sha256=dFYBK-CEtWPTJ9zKSc6EJLaQ8oHxR49nnzQiEGKeyOo,1996
13
13
  django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
14
- django_bulk_hooks-0.1.71.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.71.dist-info/METADATA,sha256=Ys-lGAZiQXE46EtkP8t_0DxNbqwtJPhisRDa2LWe-HA,5930
16
- django_bulk_hooks-0.1.71.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.71.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.73.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.73.dist-info/METADATA,sha256=NFnctdgus6n6e96VVcR1IeNh8DDVa1WzI6XyDr2iHd4,5930
16
+ django_bulk_hooks-0.1.73.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
+ django_bulk_hooks-0.1.73.dist-info/RECORD,,