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

Files changed (17) hide show
  1. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/PKG-INFO +1 -1
  2. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/models.py +6 -4
  3. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/queryset.py +36 -26
  4. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/pyproject.toml +1 -1
  5. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/LICENSE +0 -0
  6. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/README.md +0 -0
  7. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/__init__.py +0 -0
  8. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/conditions.py +0 -0
  9. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/constants.py +0 -0
  10. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/context.py +0 -0
  11. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/decorators.py +0 -0
  12. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/engine.py +0 -0
  13. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/enums.py +0 -0
  14. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/handler.py +0 -0
  15. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/manager.py +0 -0
  16. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/priority.py +0 -0
  17. {django_bulk_hooks-0.1.169 → django_bulk_hooks-0.1.171}/django_bulk_hooks/registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.169
3
+ Version: 0.1.171
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  Home-page: https://github.com/AugendLimited/django-bulk-hooks
6
6
  License: MIT
@@ -44,7 +44,8 @@ class HookModelMixin(models.Model):
44
44
  else:
45
45
  # For update operations, run VALIDATE_UPDATE hooks for validation
46
46
  try:
47
- old_instance = self.__class__.objects.get(pk=self.pk)
47
+ # Use _base_manager to avoid triggering hooks recursively
48
+ old_instance = self.__class__._base_manager.get(pk=self.pk)
48
49
  ctx = HookContext(self.__class__)
49
50
  run(self.__class__, VALIDATE_UPDATE, [self], [old_instance], ctx=ctx)
50
51
  except self.__class__.DoesNotExist:
@@ -56,7 +57,7 @@ class HookModelMixin(models.Model):
56
57
  # If bypass_hooks is True, use base manager to avoid triggering hooks
57
58
  if bypass_hooks:
58
59
  return self._base_manager.save(self, *args, **kwargs)
59
-
60
+
60
61
  is_create = self.pk is None
61
62
 
62
63
  if is_create:
@@ -70,7 +71,8 @@ class HookModelMixin(models.Model):
70
71
  else:
71
72
  # For update operations, we need to get the old record
72
73
  try:
73
- old_instance = self.__class__.objects.get(pk=self.pk)
74
+ # Use _base_manager to avoid triggering hooks recursively
75
+ old_instance = self.__class__._base_manager.get(pk=self.pk)
74
76
  ctx = HookContext(self.__class__)
75
77
  run(self.__class__, BEFORE_UPDATE, [self], [old_instance], ctx=ctx)
76
78
 
@@ -92,7 +94,7 @@ class HookModelMixin(models.Model):
92
94
  # If bypass_hooks is True, use base manager to avoid triggering hooks
93
95
  if bypass_hooks:
94
96
  return self._base_manager.delete(self, *args, **kwargs)
95
-
97
+
96
98
  ctx = HookContext(self.__class__)
97
99
 
98
100
  # Run validation hooks first
@@ -24,8 +24,9 @@ class HookQuerySet(models.QuerySet):
24
24
  objs = list(self)
25
25
  if not objs:
26
26
  return 0
27
- # Call the base QuerySet implementation to avoid recursion
28
- return super().bulk_delete(objs)
27
+ # Use our bulk_delete method to handle hooks properly
28
+ self.bulk_delete(objs)
29
+ return len(objs)
29
30
 
30
31
  @transaction.atomic
31
32
  def update(self, **kwargs):
@@ -33,33 +34,28 @@ class HookQuerySet(models.QuerySet):
33
34
  if not instances:
34
35
  return 0
35
36
 
36
- model_cls = self.model
37
- pks = [obj.pk for obj in instances]
38
-
39
- # Load originals for hook comparison and ensure they match the order of instances
40
- # Use the base manager to avoid recursion
41
- original_map = {
42
- obj.pk: obj for obj in model_cls._base_manager.filter(pk__in=pks)
43
- }
44
- originals = [original_map.get(obj.pk) for obj in instances]
45
-
46
37
  # Apply field updates to instances
47
38
  for obj in instances:
48
39
  for field, value in kwargs.items():
49
40
  setattr(obj, field, value)
50
41
 
51
- # Run BEFORE_UPDATE hooks
52
- ctx = HookContext(model_cls)
53
- engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
54
-
55
- # Use Django's built-in update logic directly
56
- # Call the base QuerySet implementation to avoid recursion
57
- update_count = super().update(**kwargs)
42
+ # Use our bulk_update method to handle hooks properly
43
+ self.bulk_update(instances, list(kwargs.keys()))
44
+ return len(instances)
58
45
 
59
- # Run AFTER_UPDATE hooks
60
- engine.run(model_cls, AFTER_UPDATE, instances, originals, ctx=ctx)
61
-
62
- return update_count
46
+ def save(self, obj):
47
+ """
48
+ Save a single object using the appropriate bulk operation.
49
+ """
50
+ if obj.pk:
51
+ # Use bulk_update for existing objects
52
+ self.bulk_update(
53
+ [obj], [field.name for field in obj._meta.fields if field.name != "id"]
54
+ )
55
+ else:
56
+ # Use bulk_create for new objects
57
+ self.bulk_create([obj])
58
+ return obj
63
59
 
64
60
  @transaction.atomic
65
61
  def bulk_create(
@@ -254,9 +250,23 @@ class HookQuerySet(models.QuerySet):
254
250
 
255
251
  pks = [obj.pk for obj in objs if obj.pk is not None]
256
252
 
257
- # Call the base QuerySet implementation to avoid recursion
258
- # The hooks have already been fired above, so we don't need them again
259
- super().bulk_delete(objs, batch_size=batch_size)
253
+ # Use Django's base manager to perform the actual deletion
254
+ # This avoids recursion and uses Django's built-in delete logic
255
+ from django.db.models import QuerySet
256
+
257
+ base_qs = QuerySet(model_cls, using=self.db)
258
+
259
+ # Delete in batches if batch_size is specified
260
+ if batch_size:
261
+ for i in range(0, len(objs), batch_size):
262
+ batch = objs[i : i + batch_size]
263
+ batch_pks = [obj.pk for obj in batch if obj.pk is not None]
264
+ if batch_pks:
265
+ base_qs.filter(pk__in=batch_pks).delete()
266
+ else:
267
+ # Delete all at once
268
+ if pks:
269
+ base_qs.filter(pk__in=pks).delete()
260
270
 
261
271
  if not bypass_hooks:
262
272
  engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-bulk-hooks"
3
- version = "0.1.169"
3
+ version = "0.1.171"
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"