django-bulk-hooks 0.1.213__tar.gz → 0.1.215__tar.gz
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-0.1.213 → django_bulk_hooks-0.1.215}/PKG-INFO +3 -3
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/conditions.py +15 -1
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/context.py +2 -1
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/engine.py +18 -1
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/queryset.py +13 -4
- django_bulk_hooks-0.1.215/django_bulk_hooks/registry.py +37 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/pyproject.toml +1 -1
- django_bulk_hooks-0.1.213/django_bulk_hooks/registry.py +0 -26
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/LICENSE +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/README.md +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/__init__.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/constants.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/decorators.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/enums.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/handler.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/manager.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/models.py +0 -0
- {django_bulk_hooks-0.1.213 → django_bulk_hooks-0.1.215}/django_bulk_hooks/priority.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: django-bulk-hooks
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.215
|
|
4
4
|
Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
|
|
5
|
-
Home-page: https://github.com/AugendLimited/django-bulk-hooks
|
|
6
5
|
License: MIT
|
|
7
6
|
Keywords: django,bulk,hooks
|
|
8
7
|
Author: Konrad Beck
|
|
@@ -14,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
15
|
Requires-Dist: Django (>=4.0)
|
|
16
|
+
Project-URL: Homepage, https://github.com/AugendLimited/django-bulk-hooks
|
|
17
17
|
Project-URL: Repository, https://github.com/AugendLimited/django-bulk-hooks
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
|
|
@@ -66,11 +66,25 @@ class HasChanged(HookCondition):
|
|
|
66
66
|
self.has_changed = has_changed
|
|
67
67
|
|
|
68
68
|
def check(self, instance, original_instance=None):
|
|
69
|
+
print(f"DEBUG: HasChanged.check called for field '{self.field}' on instance {getattr(instance, 'pk', 'No PK')}")
|
|
70
|
+
print(f"DEBUG: Original instance: {getattr(original_instance, 'pk', 'No PK') if original_instance else 'None'}")
|
|
71
|
+
|
|
69
72
|
if not original_instance:
|
|
73
|
+
print(f"DEBUG: No original instance, returning False")
|
|
70
74
|
return False
|
|
75
|
+
|
|
71
76
|
current = resolve_dotted_attr(instance, self.field)
|
|
72
77
|
previous = resolve_dotted_attr(original_instance, self.field)
|
|
73
|
-
|
|
78
|
+
|
|
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
|
+
result = (current != previous) == self.has_changed
|
|
85
|
+
print(f"DEBUG: HasChanged result: current={current}, previous={previous}, has_changed={self.has_changed}, result={result}")
|
|
86
|
+
|
|
87
|
+
return result
|
|
74
88
|
|
|
75
89
|
|
|
76
90
|
class WasEqual(HookCondition):
|
|
@@ -29,6 +29,12 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
29
29
|
print(f"DEBUG: Call stack (last 3 frames):")
|
|
30
30
|
for line in stack[-4:-1]: # Show last 3 frames before this one
|
|
31
31
|
print(f" {line.strip()}")
|
|
32
|
+
print(f"DEBUG: Total hooks found: {len(hooks)}")
|
|
33
|
+
|
|
34
|
+
# Check if we're in a bypass context
|
|
35
|
+
if ctx and hasattr(ctx, 'bypass_hooks') and ctx.bypass_hooks:
|
|
36
|
+
print(f"DEBUG: Context has bypass_hooks=True, skipping hook execution")
|
|
37
|
+
return
|
|
32
38
|
|
|
33
39
|
# For BEFORE_* events, run model.clean() first for validation
|
|
34
40
|
if event.startswith("before_"):
|
|
@@ -41,6 +47,7 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
41
47
|
|
|
42
48
|
# Process hooks
|
|
43
49
|
for handler_cls, method_name, condition, priority in hooks:
|
|
50
|
+
print(f"DEBUG: Processing hook {handler_cls.__name__}.{method_name} with condition: {condition}")
|
|
44
51
|
handler_instance = handler_cls()
|
|
45
52
|
func = getattr(handler_instance, method_name)
|
|
46
53
|
|
|
@@ -52,9 +59,16 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
52
59
|
old_records or [None] * len(new_records),
|
|
53
60
|
strict=True,
|
|
54
61
|
):
|
|
55
|
-
if not condition
|
|
62
|
+
if not condition:
|
|
63
|
+
print(f"DEBUG: No condition, adding record {new.pk if hasattr(new, 'pk') else 'No PK'}")
|
|
56
64
|
to_process_new.append(new)
|
|
57
65
|
to_process_old.append(original)
|
|
66
|
+
else:
|
|
67
|
+
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
|
+
if condition_result:
|
|
70
|
+
to_process_new.append(new)
|
|
71
|
+
to_process_old.append(original)
|
|
58
72
|
|
|
59
73
|
if to_process_new:
|
|
60
74
|
print(
|
|
@@ -66,4 +80,7 @@ def run(model_cls, event, new_records, old_records=None, ctx=None):
|
|
|
66
80
|
old_records=to_process_old if any(to_process_old) else None,
|
|
67
81
|
)
|
|
68
82
|
except Exception as e:
|
|
83
|
+
print(f"DEBUG: Hook execution failed: {e}")
|
|
69
84
|
raise
|
|
85
|
+
else:
|
|
86
|
+
print(f"DEBUG: No records to process for hook {handler_cls.__name__}.{method_name}")
|
|
@@ -161,10 +161,12 @@ class HookQuerySetMixin:
|
|
|
161
161
|
|
|
162
162
|
# Fire hooks before DB ops
|
|
163
163
|
if not bypass_hooks:
|
|
164
|
-
ctx = HookContext(model_cls)
|
|
164
|
+
ctx = HookContext(model_cls, bypass_hooks=False)
|
|
165
165
|
if not bypass_validation:
|
|
166
166
|
engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
|
|
167
167
|
engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
|
|
168
|
+
else:
|
|
169
|
+
ctx = HookContext(model_cls, bypass_hooks=True)
|
|
168
170
|
|
|
169
171
|
# For MTI models, we need to handle them specially
|
|
170
172
|
if is_mti:
|
|
@@ -219,6 +221,10 @@ class HookQuerySetMixin:
|
|
|
219
221
|
f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
220
222
|
)
|
|
221
223
|
|
|
224
|
+
print(f"DEBUG: bulk_update called with bypass_hooks={bypass_hooks} (type: {type(bypass_hooks)}), bypass_validation={bypass_validation}")
|
|
225
|
+
print(f"DEBUG: bulk_update for model {model_cls.__name__} with {len(objs)} objects")
|
|
226
|
+
print(f"DEBUG: kwargs: {kwargs}")
|
|
227
|
+
|
|
222
228
|
# Check for MTI
|
|
223
229
|
is_mti = False
|
|
224
230
|
for parent in model_cls._meta.all_parents:
|
|
@@ -237,7 +243,7 @@ class HookQuerySetMixin:
|
|
|
237
243
|
}
|
|
238
244
|
originals = [original_map.get(obj.pk) for obj in objs]
|
|
239
245
|
|
|
240
|
-
ctx = HookContext(model_cls)
|
|
246
|
+
ctx = HookContext(model_cls, bypass_hooks=False)
|
|
241
247
|
|
|
242
248
|
# Run validation hooks first
|
|
243
249
|
if not bypass_validation:
|
|
@@ -254,6 +260,7 @@ class HookQuerySetMixin:
|
|
|
254
260
|
fields = list(fields_set)
|
|
255
261
|
else:
|
|
256
262
|
print(f"DEBUG: bypass_hooks=True, skipping hooks for {len(objs)} objects")
|
|
263
|
+
ctx = HookContext(model_cls, bypass_hooks=True)
|
|
257
264
|
|
|
258
265
|
# Handle auto_now fields like Django's update_or_create does
|
|
259
266
|
fields_set = set(fields)
|
|
@@ -278,10 +285,12 @@ class HookQuerySetMixin:
|
|
|
278
285
|
for k, v in kwargs.items()
|
|
279
286
|
if k not in ["bypass_hooks", "bypass_validation"]
|
|
280
287
|
}
|
|
288
|
+
print(f"DEBUG: Calling Django's bulk_update with kwargs: {django_kwargs}")
|
|
281
289
|
# Call Django's bulk_update with hook suspension to prevent double execution
|
|
282
290
|
# Django's bulk_update internally calls .update() which would trigger our hooks again
|
|
283
|
-
|
|
284
|
-
|
|
291
|
+
print(f"DEBUG: About to call Django's bulk_update")
|
|
292
|
+
result = super().bulk_update(objs, fields, **django_kwargs)
|
|
293
|
+
print(f"DEBUG: Django's bulk_update completed, result: {result}")
|
|
285
294
|
|
|
286
295
|
if not bypass_hooks:
|
|
287
296
|
print(
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from typing import Union
|
|
3
|
+
|
|
4
|
+
from django_bulk_hooks.priority import Priority
|
|
5
|
+
|
|
6
|
+
_hooks: dict[tuple[type, str], list[tuple[type, str, Callable, int]]] = {}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register_hook(
|
|
10
|
+
model, event, handler_cls, method_name, condition, priority: Union[int, Priority]
|
|
11
|
+
):
|
|
12
|
+
key = (model, event)
|
|
13
|
+
hooks = _hooks.setdefault(key, [])
|
|
14
|
+
hooks.append((handler_cls, method_name, condition, priority))
|
|
15
|
+
# keep sorted by priority
|
|
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)}")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_hooks(model, event):
|
|
23
|
+
key = (model, event)
|
|
24
|
+
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}")
|
|
32
|
+
return hooks
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def list_all_hooks():
|
|
36
|
+
"""Debug function to list all registered hooks"""
|
|
37
|
+
return _hooks
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "django-bulk-hooks"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.215"
|
|
4
4
|
description = "Hook-style hooks for Django bulk operations like bulk_create and bulk_update."
|
|
5
5
|
authors = ["Konrad Beck <konrad.beck@merchantcapital.co.za>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
|
-
from typing import Union
|
|
3
|
-
|
|
4
|
-
from django_bulk_hooks.priority import Priority
|
|
5
|
-
|
|
6
|
-
_hooks: dict[tuple[type, str], list[tuple[type, str, Callable, int]]] = {}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def register_hook(
|
|
10
|
-
model, event, handler_cls, method_name, condition, priority: Union[int, Priority]
|
|
11
|
-
):
|
|
12
|
-
key = (model, event)
|
|
13
|
-
hooks = _hooks.setdefault(key, [])
|
|
14
|
-
hooks.append((handler_cls, method_name, condition, priority))
|
|
15
|
-
# keep sorted by priority
|
|
16
|
-
hooks.sort(key=lambda x: x[3])
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def get_hooks(model, event):
|
|
20
|
-
hooks = _hooks.get((model, event), [])
|
|
21
|
-
return hooks
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def list_all_hooks():
|
|
25
|
-
"""Debug function to list all registered hooks"""
|
|
26
|
-
return _hooks
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|