django-bulk-hooks 0.1.183__tar.gz → 0.1.185__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.183 → django_bulk_hooks-0.1.185}/PKG-INFO +1 -1
  2. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/queryset.py +61 -8
  3. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/pyproject.toml +1 -1
  4. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/LICENSE +0 -0
  5. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/README.md +0 -0
  6. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/__init__.py +0 -0
  7. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/conditions.py +0 -0
  8. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/constants.py +0 -0
  9. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/context.py +0 -0
  10. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/decorators.py +0 -0
  11. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/engine.py +0 -0
  12. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/enums.py +0 -0
  13. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/handler.py +0 -0
  14. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/manager.py +0 -0
  15. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/models.py +0 -0
  16. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/priority.py +0 -0
  17. {django_bulk_hooks-0.1.183 → django_bulk_hooks-0.1.185}/django_bulk_hooks/registry.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.183
3
+ Version: 0.1.185
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
@@ -537,6 +537,19 @@ class HookQuerySet(models.QuerySet):
537
537
  model_cls = self.model
538
538
  inheritance_chain = self._get_inheritance_chain()
539
539
 
540
+ # Remove custom hook kwargs before passing to Django internals
541
+ django_kwargs = {
542
+ k: v
543
+ for k, v in kwargs.items()
544
+ if k not in ["bypass_hooks", "bypass_validation"]
545
+ }
546
+
547
+ # Safety check to prevent infinite recursion
548
+ if len(inheritance_chain) > 10: # Arbitrary limit to prevent infinite loops
549
+ raise ValueError(
550
+ "Inheritance chain too deep - possible infinite recursion detected"
551
+ )
552
+
540
553
  # Group fields by model in the inheritance chain
541
554
  field_groups = {}
542
555
  for field_name in fields:
@@ -549,32 +562,72 @@ class HookQuerySet(models.QuerySet):
549
562
  field_groups[model].append(field_name)
550
563
  break
551
564
 
552
- # Update each table in the inheritance chain
565
+ # Process in batches
566
+ batch_size = django_kwargs.get("batch_size") or len(objs)
553
567
  total_updated = 0
568
+
569
+ with transaction.atomic(using=self.db, savepoint=False):
570
+ for i in range(0, len(objs), batch_size):
571
+ batch = objs[i : i + batch_size]
572
+ batch_result = self._process_mti_bulk_update_batch(
573
+ batch, field_groups, inheritance_chain, **django_kwargs
574
+ )
575
+ total_updated += batch_result
576
+
577
+ return total_updated
578
+
579
+ def _process_mti_bulk_update_batch(self, batch, field_groups, inheritance_chain, **kwargs):
580
+ """
581
+ Process a single batch of objects for MTI bulk update.
582
+ Updates each table in the inheritance chain for the batch.
583
+ """
584
+ total_updated = 0
585
+
586
+ # Update each table in the inheritance chain
554
587
  for model, model_fields in field_groups.items():
555
588
  if not model_fields:
556
589
  continue
557
590
 
558
- # Use Django's update() with CASE statements for bulk update
559
- pks = [obj.pk for obj in objs]
591
+ # For MTI, we need to handle parent links correctly
592
+ # The root model (first in chain) has its own PK
593
+ # Child models use the parent link to reference the root PK
594
+ if model == inheritance_chain[0]:
595
+ # Root model - use primary keys directly
596
+ pks = [obj.pk for obj in batch]
597
+ filter_field = 'pk'
598
+ else:
599
+ # Child model - use parent link field
600
+ parent_link = None
601
+ for parent_model in inheritance_chain:
602
+ if parent_model in model._meta.parents:
603
+ parent_link = model._meta.parents[parent_model]
604
+ break
605
+
606
+ if parent_link is None:
607
+ # This shouldn't happen in proper MTI, but handle gracefully
608
+ continue
609
+
610
+ # Get the parent link values (these should be the same as the root PKs)
611
+ pks = [getattr(obj, parent_link.attname) for obj in batch]
612
+ filter_field = parent_link.attname
560
613
 
561
614
  if pks:
562
615
  base_qs = model._base_manager.using(self.db)
563
616
 
564
- # Build CASE statements for each field
617
+ # Build CASE statements for each field to perform a single bulk update
565
618
  case_statements = {}
566
619
  for field_name in model_fields:
567
620
  field = model._meta.get_field(field_name)
568
621
  when_statements = []
569
622
 
570
- for pk, obj in zip(pks, objs):
623
+ for pk, obj in zip(pks, batch):
571
624
  value = getattr(obj, field_name)
572
- when_statements.append(When(pk=pk, then=Value(value, output_field=field)))
625
+ when_statements.append(When(**{filter_field: pk}, then=Value(value, output_field=field)))
573
626
 
574
627
  case_statements[field_name] = Case(*when_statements, output_field=field)
575
628
 
576
- # Execute the update
577
- updated_count = base_qs.filter(pk__in=pks).update(**case_statements)
629
+ # Execute a single bulk update for all objects in this model
630
+ updated_count = base_qs.filter(**{f"{filter_field}__in": pks}).update(**case_statements)
578
631
  total_updated += updated_count
579
632
 
580
633
  return total_updated
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-bulk-hooks"
3
- version = "0.1.183"
3
+ version = "0.1.185"
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"