django-bulk-hooks 0.2.5__tar.gz → 0.2.7__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 (26) hide show
  1. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/PKG-INFO +1 -1
  2. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/bulk_executor.py +7 -0
  3. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/coordinator.py +90 -3
  4. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/pyproject.toml +1 -1
  5. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/LICENSE +0 -0
  6. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/README.md +0 -0
  7. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/__init__.py +0 -0
  8. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/changeset.py +0 -0
  9. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/conditions.py +0 -0
  10. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/constants.py +0 -0
  11. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/context.py +0 -0
  12. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/debug_utils.py +0 -0
  13. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/decorators.py +0 -0
  14. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/dispatcher.py +0 -0
  15. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/enums.py +0 -0
  16. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/factory.py +0 -0
  17. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/handler.py +0 -0
  18. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/helpers.py +0 -0
  19. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/manager.py +0 -0
  20. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/models.py +0 -0
  21. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/__init__.py +0 -0
  22. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/analyzer.py +0 -0
  23. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/mti_handler.py +0 -0
  24. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/operations/mti_plans.py +0 -0
  25. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/django_bulk_hooks/queryset.py +0 -0
  26. {django_bulk_hooks-0.2.5 → django_bulk_hooks-0.2.7}/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.5
3
+ Version: 0.2.7
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
@@ -386,6 +386,13 @@ class BulkExecutor:
386
386
  continue
387
387
 
388
388
  value = getattr(obj, db_field_name)
389
+
390
+ # For FK fields, ensure we get the actual ID value, not the related object
391
+ if getattr(field, 'is_relation', False) and hasattr(field, 'attname'):
392
+ # If value is a model instance, get its pk
393
+ if value is not None and hasattr(value, 'pk'):
394
+ value = value.pk
395
+
389
396
  when_statements.append(
390
397
  When(
391
398
  **{field_group.filter_field: pk},
@@ -143,7 +143,7 @@ class BulkOperationCoordinator:
143
143
  unique_fields=unique_fields,
144
144
  )
145
145
 
146
- return self.dispatcher.execute_operation_with_hooks(
146
+ return self._execute_with_mti_hooks(
147
147
  changeset=changeset,
148
148
  operation=operation,
149
149
  event_prefix="create",
@@ -199,7 +199,7 @@ class BulkOperationCoordinator:
199
199
  def operation():
200
200
  return self.executor.bulk_update(objs, fields, batch_size=batch_size)
201
201
 
202
- return self.dispatcher.execute_operation_with_hooks(
202
+ return self._execute_with_mti_hooks(
203
203
  changeset=changeset,
204
204
  operation=operation,
205
205
  event_prefix="update",
@@ -343,7 +343,7 @@ class BulkOperationCoordinator:
343
343
  # Call base Django QuerySet.delete() to avoid recursion
344
344
  return BaseQuerySet.delete(self.queryset)
345
345
 
346
- return self.dispatcher.execute_operation_with_hooks(
346
+ return self._execute_with_mti_hooks(
347
347
  changeset=changeset,
348
348
  operation=operation,
349
349
  event_prefix="delete",
@@ -383,3 +383,90 @@ class BulkOperationCoordinator:
383
383
 
384
384
  # Dispatch validation event only
385
385
  self.dispatcher.dispatch(changeset, event, bypass_hooks=False)
386
+
387
+ # ==================== MTI PARENT HOOK SUPPORT ====================
388
+
389
+ def _build_changeset_for_model(self, original_changeset, target_model_cls):
390
+ """
391
+ Build a changeset for a specific model in the MTI inheritance chain.
392
+
393
+ This allows parent model hooks to receive the same instances but with
394
+ the correct model_cls for hook registration matching.
395
+
396
+ Args:
397
+ original_changeset: The original changeset (for child model)
398
+ target_model_cls: The model class to build changeset for (parent model)
399
+
400
+ Returns:
401
+ ChangeSet for the target model
402
+ """
403
+ from django_bulk_hooks.changeset import ChangeSet
404
+
405
+ # Create new changeset with target model but same record changes
406
+ return ChangeSet(
407
+ model_cls=target_model_cls,
408
+ changes=original_changeset.changes,
409
+ operation_type=original_changeset.operation_type,
410
+ operation_meta=original_changeset.operation_meta,
411
+ )
412
+
413
+ def _execute_with_mti_hooks(
414
+ self,
415
+ changeset,
416
+ operation,
417
+ event_prefix,
418
+ bypass_hooks=False,
419
+ bypass_validation=False
420
+ ):
421
+ """
422
+ Execute operation with hooks for entire MTI inheritance chain.
423
+
424
+ This method dispatches hooks for both child and parent models when
425
+ dealing with MTI models, ensuring parent model hooks fire when
426
+ child instances are created/updated/deleted.
427
+
428
+ Args:
429
+ changeset: ChangeSet for the child model
430
+ operation: Callable that performs the actual DB operation
431
+ event_prefix: 'create', 'update', or 'delete'
432
+ bypass_hooks: Skip all hooks if True
433
+ bypass_validation: Skip validation hooks if True
434
+
435
+ Returns:
436
+ Result of operation
437
+ """
438
+ if bypass_hooks:
439
+ return operation()
440
+
441
+ # Get all models in inheritance chain
442
+ models_in_chain = [changeset.model_cls]
443
+ if self.mti_handler.is_mti_model():
444
+ parent_models = self.mti_handler.get_parent_models()
445
+ models_in_chain.extend(parent_models)
446
+
447
+ # VALIDATE phase - for all models in chain
448
+ if not bypass_validation:
449
+ for model_cls in models_in_chain:
450
+ model_changeset = self._build_changeset_for_model(changeset, model_cls)
451
+ self.dispatcher.dispatch(model_changeset, f"validate_{event_prefix}", bypass_hooks=False)
452
+
453
+ # BEFORE phase - for all models in chain
454
+ for model_cls in models_in_chain:
455
+ model_changeset = self._build_changeset_for_model(changeset, model_cls)
456
+ self.dispatcher.dispatch(model_changeset, f"before_{event_prefix}", bypass_hooks=False)
457
+
458
+ # Execute the actual operation
459
+ result = operation()
460
+
461
+ # AFTER phase - for all models in chain
462
+ # Use result if operation returns modified data (for create operations)
463
+ if result and isinstance(result, list) and event_prefix == "create":
464
+ # Rebuild changeset with assigned PKs for AFTER hooks
465
+ from django_bulk_hooks.helpers import build_changeset_for_create
466
+ changeset = build_changeset_for_create(changeset.model_cls, result)
467
+
468
+ for model_cls in models_in_chain:
469
+ model_changeset = self._build_changeset_for_model(changeset, model_cls)
470
+ self.dispatcher.dispatch(model_changeset, f"after_{event_prefix}", bypass_hooks=False)
471
+
472
+ return result
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "django-bulk-hooks"
3
- version = "0.2.5"
3
+ version = "0.2.7"
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"