django-bulk-hooks 0.1.85__tar.gz → 0.1.86__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.85
3
+ Version: 0.1.86
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
@@ -8,7 +8,12 @@ from django_bulk_hooks.conditions import safe_get_related_object, safe_get_relat
8
8
  logger = logging.getLogger(__name__)
9
9
 
10
10
 
11
+ # Cache for hook handlers to avoid creating them repeatedly
12
+ _handler_cache = {}
13
+
11
14
  def run(model_cls, event, new_instances, original_instances=None, ctx=None):
15
+ # Get hooks from cache or fetch them
16
+ cache_key = (model_cls, event)
12
17
  hooks = get_hooks(model_cls, event)
13
18
 
14
19
  if not hooks:
@@ -32,19 +37,32 @@ def run(model_cls, event, new_instances, original_instances=None, ctx=None):
32
37
  logger.error("Unexpected error during validation for %s: %s", instance, e)
33
38
  raise
34
39
 
40
+ # Pre-create None list for originals if needed
41
+ if original_instances is None:
42
+ original_instances = [None] * len(new_instances)
43
+
44
+ # Process all hooks
35
45
  for handler_cls, method_name, condition, priority in hooks:
36
- handler_instance = handler_cls()
37
- func = getattr(handler_instance, method_name)
46
+ # Get or create handler instance from cache
47
+ handler_key = (handler_cls, method_name)
48
+ if handler_key not in _handler_cache:
49
+ handler_instance = handler_cls()
50
+ func = getattr(handler_instance, method_name)
51
+ _handler_cache[handler_key] = (handler_instance, func)
52
+ else:
53
+ handler_instance, func = _handler_cache[handler_key]
54
+
55
+ # If no condition, process all instances at once
56
+ if not condition:
57
+ func(new_records=new_instances, old_records=original_instances if any(original_instances) else None)
58
+ continue
38
59
 
60
+ # For conditional hooks, filter instances first
39
61
  to_process_new = []
40
62
  to_process_old = []
41
63
 
42
- for new, original in zip(
43
- new_instances,
44
- original_instances or [None] * len(new_instances),
45
- strict=True,
46
- ):
47
- if not condition or condition.check(new, original):
64
+ for new, original in zip(new_instances, original_instances, strict=True):
65
+ if condition.check(new, original):
48
66
  to_process_new.append(new)
49
67
  to_process_old.append(original)
50
68
 
@@ -91,40 +91,24 @@ class HookModelMixin(models.Model):
91
91
  is_create = self.pk is None
92
92
  ctx = HookContext(self.__class__)
93
93
 
94
- if is_create:
95
- # For create operations, run BEFORE hooks first
96
- with patch_foreign_key_behavior():
94
+ # Use a single context manager for all hooks
95
+ with patch_foreign_key_behavior():
96
+ if is_create:
97
+ # For create operations
97
98
  run(self.__class__, BEFORE_CREATE, [self], ctx=ctx)
98
-
99
- # Then let Django save
100
- super().save(*args, **kwargs)
101
-
102
- # Then run AFTER hooks
103
- with patch_foreign_key_behavior():
99
+ super().save(*args, **kwargs)
104
100
  run(self.__class__, AFTER_CREATE, [self], ctx=ctx)
105
- else:
106
- # For update operations, we need to get the old record
107
- try:
108
- old_instance = self.__class__.objects.get(pk=self.pk)
109
-
110
- # Run BEFORE hooks first
111
- with patch_foreign_key_behavior():
101
+ else:
102
+ # For update operations
103
+ try:
104
+ old_instance = self.__class__.objects.get(pk=self.pk)
112
105
  run(self.__class__, BEFORE_UPDATE, [self], [old_instance], ctx=ctx)
113
-
114
- # Then let Django save
115
- super().save(*args, **kwargs)
116
-
117
- # Then run AFTER hooks
118
- with patch_foreign_key_behavior():
106
+ super().save(*args, **kwargs)
119
107
  run(self.__class__, AFTER_UPDATE, [self], [old_instance], ctx=ctx)
120
- except self.__class__.DoesNotExist:
121
- # If the old instance doesn't exist, treat as create
122
- with patch_foreign_key_behavior():
108
+ except self.__class__.DoesNotExist:
109
+ # If the old instance doesn't exist, treat as create
123
110
  run(self.__class__, BEFORE_CREATE, [self], ctx=ctx)
124
-
125
- super().save(*args, **kwargs)
126
-
127
- with patch_foreign_key_behavior():
111
+ super().save(*args, **kwargs)
128
112
  run(self.__class__, AFTER_CREATE, [self], ctx=ctx)
129
113
 
130
114
  return self
@@ -132,16 +116,11 @@ class HookModelMixin(models.Model):
132
116
  def delete(self, *args, **kwargs):
133
117
  ctx = HookContext(self.__class__)
134
118
 
135
- # Run validation hooks first
119
+ # Use a single context manager for all hooks
136
120
  with patch_foreign_key_behavior():
137
121
  run(self.__class__, VALIDATE_DELETE, [self], ctx=ctx)
138
-
139
- # Then run business logic hooks
140
- with patch_foreign_key_behavior():
141
122
  run(self.__class__, BEFORE_DELETE, [self], ctx=ctx)
142
-
143
- result = super().delete(*args, **kwargs)
144
-
145
- with patch_foreign_key_behavior():
123
+ result = super().delete(*args, **kwargs)
146
124
  run(self.__class__, AFTER_DELETE, [self], ctx=ctx)
125
+
147
126
  return result
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-bulk-hooks"
3
- version = "0.1.85"
3
+ version = "0.1.86"
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"