django-bulk-hooks 0.1.218__py3-none-any.whl → 0.1.220__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/conditions.py +9 -4
- django_bulk_hooks/engine.py +5 -5
- django_bulk_hooks/models.py +6 -7
- django_bulk_hooks/queryset.py +15 -12
- django_bulk_hooks/registry.py +5 -2
- {django_bulk_hooks-0.1.218.dist-info → django_bulk_hooks-0.1.220.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.218.dist-info → django_bulk_hooks-0.1.220.dist-info}/RECORD +9 -9
- {django_bulk_hooks-0.1.218.dist-info → django_bulk_hooks-0.1.220.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.218.dist-info → django_bulk_hooks-0.1.220.dist-info}/WHEEL +0 -0
django_bulk_hooks/conditions.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
logger = logging.getLogger(__name__)
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
def resolve_dotted_attr(instance, dotted_path):
|
|
2
7
|
"""
|
|
3
8
|
Recursively resolve a dotted attribute path, e.g., "type.category".
|
|
@@ -66,11 +71,11 @@ class HasChanged(HookCondition):
|
|
|
66
71
|
self.has_changed = has_changed
|
|
67
72
|
|
|
68
73
|
def check(self, instance, original_instance=None):
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
logger.debug(f"HasChanged.check called for field '{self.field}' on instance {getattr(instance, 'pk', 'No PK')}")
|
|
75
|
+
logger.debug(f"Original instance: {getattr(original_instance, 'pk', 'No PK') if original_instance else 'None'}")
|
|
71
76
|
|
|
72
77
|
if not original_instance:
|
|
73
|
-
|
|
78
|
+
logger.debug("No original instance, returning False")
|
|
74
79
|
return False
|
|
75
80
|
|
|
76
81
|
current = resolve_dotted_attr(instance, self.field)
|
|
@@ -78,7 +83,7 @@ class HasChanged(HookCondition):
|
|
|
78
83
|
|
|
79
84
|
# Add more detailed debugging
|
|
80
85
|
result = (current != previous) == self.has_changed
|
|
81
|
-
|
|
86
|
+
logger.debug(f"HasChanged {self.field} result={result}")
|
|
82
87
|
return result
|
|
83
88
|
|
|
84
89
|
|
django_bulk_hooks/engine.py
CHANGED
|
@@ -23,11 +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
|
-
|
|
26
|
+
logger.debug(f"engine.run {model_cls.__name__}.{event} {len(new_records)} records")
|
|
27
27
|
|
|
28
28
|
# Check if we're in a bypass context
|
|
29
29
|
if ctx and hasattr(ctx, 'bypass_hooks') and ctx.bypass_hooks:
|
|
30
|
-
|
|
30
|
+
logger.debug("engine.run bypassed")
|
|
31
31
|
return
|
|
32
32
|
|
|
33
33
|
# For BEFORE_* events, run model.clean() first for validation
|
|
@@ -41,7 +41,7 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
41
41
|
|
|
42
42
|
# Process hooks
|
|
43
43
|
for handler_cls, method_name, condition, priority in hooks:
|
|
44
|
-
|
|
44
|
+
logger.debug(f"Processing {handler_cls.__name__}.{method_name}")
|
|
45
45
|
handler_instance = handler_cls()
|
|
46
46
|
func = getattr(handler_instance, method_name)
|
|
47
47
|
|
|
@@ -63,12 +63,12 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
63
63
|
to_process_old.append(original)
|
|
64
64
|
|
|
65
65
|
if to_process_new:
|
|
66
|
-
|
|
66
|
+
logger.debug(f"Executing {handler_cls.__name__}.{method_name} for {len(to_process_new)} records")
|
|
67
67
|
try:
|
|
68
68
|
func(
|
|
69
69
|
new_records=to_process_new,
|
|
70
70
|
old_records=to_process_old if any(to_process_old) else None,
|
|
71
71
|
)
|
|
72
72
|
except Exception as e:
|
|
73
|
-
|
|
73
|
+
logger.debug(f"Hook execution failed: {e}")
|
|
74
74
|
raise
|
django_bulk_hooks/models.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from django.db import models
|
|
2
3
|
|
|
3
4
|
from django_bulk_hooks.constants import (
|
|
@@ -15,6 +16,8 @@ from django_bulk_hooks.context import HookContext
|
|
|
15
16
|
from django_bulk_hooks.engine import run
|
|
16
17
|
from django_bulk_hooks.manager import BulkHookManager
|
|
17
18
|
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
class HookModelMixin(models.Model):
|
|
20
23
|
objects = BulkHookManager()
|
|
@@ -56,15 +59,13 @@ class HookModelMixin(models.Model):
|
|
|
56
59
|
def save(self, *args, bypass_hooks=False, **kwargs):
|
|
57
60
|
# If bypass_hooks is True, use base manager to avoid triggering hooks
|
|
58
61
|
if bypass_hooks:
|
|
59
|
-
|
|
60
|
-
f"DEBUG: save() called with bypass_hooks=True for {self.__class__.__name__} pk={self.pk}"
|
|
61
|
-
)
|
|
62
|
+
logger.debug(f"save() called with bypass_hooks=True for {self.__class__.__name__} pk={self.pk}")
|
|
62
63
|
return self._base_manager.save(self, *args, **kwargs)
|
|
63
64
|
|
|
64
65
|
is_create = self.pk is None
|
|
65
66
|
|
|
66
67
|
if is_create:
|
|
67
|
-
|
|
68
|
+
logger.debug(f"save() creating new {self.__class__.__name__} instance")
|
|
68
69
|
# For create operations, we don't have old records
|
|
69
70
|
ctx = HookContext(self.__class__)
|
|
70
71
|
run(self.__class__, BEFORE_CREATE, [self], ctx=ctx)
|
|
@@ -73,9 +74,7 @@ class HookModelMixin(models.Model):
|
|
|
73
74
|
|
|
74
75
|
run(self.__class__, AFTER_CREATE, [self], ctx=ctx)
|
|
75
76
|
else:
|
|
76
|
-
|
|
77
|
-
f"DEBUG: save() updating existing {self.__class__.__name__} instance pk={self.pk}"
|
|
78
|
-
)
|
|
77
|
+
logger.debug(f"save() updating existing {self.__class__.__name__} instance pk={self.pk}")
|
|
79
78
|
# For update operations, we need to get the old record
|
|
80
79
|
try:
|
|
81
80
|
# Use _base_manager to avoid triggering hooks recursively
|
django_bulk_hooks/queryset.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from django.db import models, transaction
|
|
2
3
|
from django.db.models import AutoField, Case, Field, Value, When
|
|
3
4
|
|
|
4
5
|
from django_bulk_hooks import engine
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
5
8
|
from django_bulk_hooks.constants import (
|
|
6
9
|
AFTER_CREATE,
|
|
7
10
|
AFTER_DELETE,
|
|
@@ -78,10 +81,10 @@ class HookQuerySetMixin:
|
|
|
78
81
|
|
|
79
82
|
# If we're in a bulk operation context, skip hooks to prevent double execution
|
|
80
83
|
if current_bypass_hooks:
|
|
81
|
-
|
|
84
|
+
logger.debug("update skipping hooks (bulk context)")
|
|
82
85
|
ctx = HookContext(model_cls, bypass_hooks=True)
|
|
83
86
|
else:
|
|
84
|
-
|
|
87
|
+
logger.debug("update running hooks (standalone)")
|
|
85
88
|
ctx = HookContext(model_cls, bypass_hooks=False)
|
|
86
89
|
# Run validation hooks first
|
|
87
90
|
engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
|
|
@@ -115,10 +118,10 @@ class HookQuerySetMixin:
|
|
|
115
118
|
|
|
116
119
|
# Run AFTER_UPDATE hooks only for standalone updates
|
|
117
120
|
if not current_bypass_hooks:
|
|
118
|
-
|
|
121
|
+
logger.debug("update running AFTER_UPDATE")
|
|
119
122
|
engine.run(model_cls, AFTER_UPDATE, instances, originals, ctx=ctx)
|
|
120
123
|
else:
|
|
121
|
-
|
|
124
|
+
logger.debug("update skipping AFTER_UPDATE (bulk context)")
|
|
122
125
|
|
|
123
126
|
return update_count
|
|
124
127
|
|
|
@@ -183,7 +186,7 @@ class HookQuerySetMixin:
|
|
|
183
186
|
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
184
187
|
else:
|
|
185
188
|
ctx = HookContext(model_cls, bypass_hooks=True) # Pass bypass_hooks
|
|
186
|
-
|
|
189
|
+
logger.debug("bulk_create bypassed hooks")
|
|
187
190
|
|
|
188
191
|
# For MTI models, we need to handle them specially
|
|
189
192
|
if is_mti:
|
|
@@ -238,7 +241,7 @@ class HookQuerySetMixin:
|
|
|
238
241
|
f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
239
242
|
)
|
|
240
243
|
|
|
241
|
-
|
|
244
|
+
logger.debug(f"bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}")
|
|
242
245
|
|
|
243
246
|
# Check for MTI
|
|
244
247
|
is_mti = False
|
|
@@ -248,11 +251,11 @@ class HookQuerySetMixin:
|
|
|
248
251
|
break
|
|
249
252
|
|
|
250
253
|
if not bypass_hooks:
|
|
251
|
-
|
|
254
|
+
logger.debug("bulk_update setting bypass_hooks=False (hooks will run in update())")
|
|
252
255
|
ctx = HookContext(model_cls, bypass_hooks=False)
|
|
253
256
|
originals = [None] * len(objs) # Placeholder for after_update call
|
|
254
257
|
else:
|
|
255
|
-
|
|
258
|
+
logger.debug("bulk_update setting bypass_hooks=True (no hooks)")
|
|
256
259
|
ctx = HookContext(model_cls, bypass_hooks=True)
|
|
257
260
|
originals = [None] * len(objs) # Ensure originals is defined for after_update call
|
|
258
261
|
|
|
@@ -279,16 +282,16 @@ class HookQuerySetMixin:
|
|
|
279
282
|
for k, v in kwargs.items()
|
|
280
283
|
if k not in ["bypass_hooks", "bypass_validation"]
|
|
281
284
|
}
|
|
282
|
-
|
|
285
|
+
logger.debug("Calling Django bulk_update")
|
|
283
286
|
result = super().bulk_update(objs, fields, **django_kwargs)
|
|
284
|
-
|
|
287
|
+
logger.debug(f"Django bulk_update done: {result}")
|
|
285
288
|
|
|
286
289
|
# Note: We don't run AFTER_UPDATE hooks here to prevent double execution
|
|
287
290
|
# The update() method will handle all hook execution based on thread-local state
|
|
288
291
|
if not bypass_hooks:
|
|
289
|
-
|
|
292
|
+
logger.debug("bulk_update skipping AFTER_UPDATE (update() will handle)")
|
|
290
293
|
else:
|
|
291
|
-
|
|
294
|
+
logger.debug("bulk_update bypassed hooks")
|
|
292
295
|
|
|
293
296
|
return result
|
|
294
297
|
|
django_bulk_hooks/registry.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from collections.abc import Callable
|
|
2
3
|
from typing import Union
|
|
3
4
|
|
|
4
5
|
from django_bulk_hooks.priority import Priority
|
|
5
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
6
9
|
_hooks: dict[tuple[type, str], list[tuple[type, str, Callable, int]]] = {}
|
|
7
10
|
|
|
8
11
|
|
|
@@ -14,13 +17,13 @@ def register_hook(
|
|
|
14
17
|
hooks.append((handler_cls, method_name, condition, priority))
|
|
15
18
|
# keep sorted by priority
|
|
16
19
|
hooks.sort(key=lambda x: x[3])
|
|
17
|
-
|
|
20
|
+
logger.debug(f"Registered {handler_cls.__name__}.{method_name} for {model.__name__}.{event}")
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
def get_hooks(model, event):
|
|
21
24
|
key = (model, event)
|
|
22
25
|
hooks = _hooks.get(key, [])
|
|
23
|
-
|
|
26
|
+
logger.debug(f"get_hooks {model.__name__}.{event} found {len(hooks)} hooks")
|
|
24
27
|
return hooks
|
|
25
28
|
|
|
26
29
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
django_bulk_hooks/__init__.py,sha256=uUgpnb9AWjIAcWNpCMqBcOewSnpJjJYH6cjPbQkzoNU,140
|
|
2
|
-
django_bulk_hooks/conditions.py,sha256=
|
|
2
|
+
django_bulk_hooks/conditions.py,sha256=hI-LfcRZBiBrmfAVJZfjEnw7F2XJ0xvkd4J3NGzMpGc,6572
|
|
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=
|
|
6
|
+
django_bulk_hooks/engine.py,sha256=t_kvgex6_iZEFc5LK-srBTZPe-1bdlYdip5LfWOc6lc,2411
|
|
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
|
-
django_bulk_hooks/models.py,sha256=
|
|
10
|
+
django_bulk_hooks/models.py,sha256=exnXYVKEVbYAXhChCP8VdWTnKCnm9DiTcokEIBee1I0,4350
|
|
11
11
|
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
|
-
django_bulk_hooks/queryset.py,sha256=
|
|
13
|
-
django_bulk_hooks/registry.py,sha256=
|
|
14
|
-
django_bulk_hooks-0.1.
|
|
15
|
-
django_bulk_hooks-0.1.
|
|
16
|
-
django_bulk_hooks-0.1.
|
|
17
|
-
django_bulk_hooks-0.1.
|
|
12
|
+
django_bulk_hooks/queryset.py,sha256=H9toczyQ4KZUGSi-M594vF3BRaSVQX-tqKb6GHe7ua0,32648
|
|
13
|
+
django_bulk_hooks/registry.py,sha256=T8ET5VGgvcRKhf5fuFXE8pNzWfkiIWN1QEGT-HyIS_E,957
|
|
14
|
+
django_bulk_hooks-0.1.220.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.220.dist-info/METADATA,sha256=zCKNbtprnajlqueQU707WJtzREhFTmKzScxioyab0-U,9061
|
|
16
|
+
django_bulk_hooks-0.1.220.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.220.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|