django-bulk-hooks 0.2.58__tar.gz → 0.2.59__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 (27) hide show
  1. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/PKG-INFO +1 -1
  2. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/coordinator.py +42 -17
  3. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/pyproject.toml +1 -1
  4. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/LICENSE +0 -0
  5. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/README.md +0 -0
  6. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/__init__.py +0 -0
  7. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/changeset.py +0 -0
  8. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/conditions.py +0 -0
  9. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/constants.py +0 -0
  10. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/context.py +0 -0
  11. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/decorators.py +0 -0
  12. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/dispatcher.py +0 -0
  13. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/enums.py +0 -0
  14. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/factory.py +0 -0
  15. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/handler.py +0 -0
  16. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/helpers.py +0 -0
  17. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/manager.py +0 -0
  18. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/models.py +0 -0
  19. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/__init__.py +0 -0
  20. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/analyzer.py +0 -0
  21. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/bulk_executor.py +0 -0
  22. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/field_utils.py +0 -0
  23. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/mti_handler.py +0 -0
  24. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/mti_plans.py +0 -0
  25. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/operations/record_classifier.py +0 -0
  26. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/django_bulk_hooks/queryset.py +0 -0
  27. {django_bulk_hooks-0.2.58 → django_bulk_hooks-0.2.59}/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.2.58
3
+ Version: 0.2.59
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
@@ -740,6 +740,8 @@ class BulkOperationCoordinator:
740
740
  if not result_objects:
741
741
  return
742
742
 
743
+ # First pass: collect objects with metadata and objects needing timestamp check
744
+ objects_needing_timestamp_check = []
743
745
  for obj in result_objects:
744
746
  # Check if metadata was set
745
747
  if hasattr(obj, "_bulk_hooks_was_created"):
@@ -749,29 +751,52 @@ class BulkOperationCoordinator:
749
751
  else:
750
752
  updated_objects.append(obj)
751
753
  else:
752
- # Fallback: if no metadata, check timestamps
754
+ # Need to check timestamps - collect for bulk query
755
+ objects_needing_timestamp_check.append(obj)
756
+
757
+ # Bulk fetch timestamps for objects without metadata (avoids N+1 queries)
758
+ if objects_needing_timestamp_check:
759
+ # Group by model class to handle MTI scenarios
760
+ objects_by_model = {}
761
+ for obj in objects_needing_timestamp_check:
753
762
  model_cls = obj.__class__
763
+ if model_cls not in objects_by_model:
764
+ objects_by_model[model_cls] = []
765
+ objects_by_model[model_cls].append(obj)
766
+
767
+ # Fetch timestamps in bulk for each model class
768
+ for model_cls, objs in objects_by_model.items():
754
769
  if hasattr(model_cls, "created_at") and hasattr(model_cls, "updated_at"):
755
- # Reload from DB to get accurate timestamps
756
- db_obj = model_cls.objects.filter(pk=obj.pk).values("created_at", "updated_at").first()
757
- if db_obj:
758
- created_at = db_obj["created_at"]
759
- updated_at = db_obj["updated_at"]
760
- if created_at and updated_at:
761
- time_diff = abs((updated_at - created_at).total_seconds())
762
- if time_diff <= 1.0: # Within 1 second = just created
763
- created_objects.append(obj)
770
+ # Bulk fetch timestamps for all objects of this model
771
+ pks = [obj.pk for obj in objs if obj.pk is not None]
772
+ if pks:
773
+ timestamp_map = {
774
+ record["pk"]: (record["created_at"], record["updated_at"])
775
+ for record in model_cls.objects.filter(pk__in=pks).values("pk", "created_at", "updated_at")
776
+ }
777
+
778
+ # Classify each object based on timestamps
779
+ for obj in objs:
780
+ if obj.pk in timestamp_map:
781
+ created_at, updated_at = timestamp_map[obj.pk]
782
+ if created_at and updated_at:
783
+ time_diff = abs((updated_at - created_at).total_seconds())
784
+ if time_diff <= 1.0: # Within 1 second = just created
785
+ created_objects.append(obj)
786
+ else:
787
+ updated_objects.append(obj)
788
+ else:
789
+ # No timestamps, default to created
790
+ created_objects.append(obj)
764
791
  else:
765
- updated_objects.append(obj)
766
- else:
767
- # No timestamps, default to created
768
- created_objects.append(obj)
792
+ # Object not found, treat as created
793
+ created_objects.append(obj)
769
794
  else:
770
- # Object not found, treat as created
771
- created_objects.append(obj)
795
+ # No PKs, default all to created
796
+ created_objects.extend(objs)
772
797
  else:
773
798
  # No timestamp fields, default to created
774
- created_objects.append(obj)
799
+ created_objects.extend(objs)
775
800
 
776
801
  logger.info(f"Upsert after hooks: {len(created_objects)} created, {len(updated_objects)} updated")
777
802
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-bulk-hooks"
3
- version = "0.2.58"
3
+ version = "0.2.59"
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"