django-bulk-hooks 0.2.72__py3-none-any.whl → 0.2.73__py3-none-any.whl

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.

@@ -191,13 +191,17 @@ class HookDispatcher:
191
191
  changeset: ChangeSet with all record changes
192
192
  event: The hook event (e.g., 'before_create')
193
193
  """
194
- # NEW: Preload relationships needed for condition evaluation
194
+ # Preload relationships needed for condition evaluation (skip if already done upfront)
195
195
  if condition:
196
- condition_relationships = self._extract_condition_relationships(condition, changeset.model_cls)
197
- logger.info(f"🔍 CONDITION: {handler_cls.__name__}.{method_name} has condition, extracted relationships: {condition_relationships}")
198
- if condition_relationships:
199
- logger.info(f"🔗 PRELOADING: Preloading condition relationships for {len(changeset.changes)} records")
200
- self._preload_condition_relationships(changeset, condition_relationships)
196
+ # Skip per-hook preloading if relationships were already preloaded upfront
197
+ if not changeset.operation_meta.get('relationships_preloaded', False):
198
+ condition_relationships = self._extract_condition_relationships(condition, changeset.model_cls)
199
+ logger.info(f"🔍 CONDITION: {handler_cls.__name__}.{method_name} has condition, extracted relationships: {condition_relationships}")
200
+ if condition_relationships:
201
+ logger.info(f"🔗 PRELOADING: Preloading condition relationships for {len(changeset.changes)} records")
202
+ self._preload_condition_relationships(changeset, condition_relationships)
203
+ else:
204
+ logger.debug(f"🔍 CONDITION: {handler_cls.__name__}.{method_name} has condition (relationships already preloaded)")
201
205
 
202
206
  # Filter records based on condition (now safe - relationships are preloaded)
203
207
  if condition:
@@ -711,6 +711,17 @@ class BulkOperationCoordinator:
711
711
  models_in_chain = self._get_models_in_chain(changeset.model_cls)
712
712
  logger.debug(f"🔗 MTI_CHAIN_START: {len(models_in_chain)} models in chain for {changeset.model_cls.__name__}")
713
713
 
714
+ # Extract and preload relationships needed by hook conditions upfront
715
+ # This prevents duplicate queries by avoiding per-hook preloading
716
+ condition_relationships = self._extract_condition_relationships_for_operation(changeset, models_in_chain)
717
+ if condition_relationships:
718
+ logger.info(f"🔗 BULK PRELOAD: Preloading {len(condition_relationships)} condition relationships for {changeset.model_cls.__name__} hooks")
719
+ self.dispatcher._preload_condition_relationships(changeset, condition_relationships)
720
+ # Mark that relationships have been preloaded to avoid per-hook duplication
721
+ changeset.operation_meta['relationships_preloaded'] = True
722
+ else:
723
+ logger.info(f"🔗 BULK PRELOAD: No condition relationships to preload for {changeset.model_cls.__name__} hooks")
724
+
714
725
  # VALIDATE phase - for all models in chain
715
726
  if not bypass_validation:
716
727
  self._dispatch_hooks_for_models(models_in_chain, changeset, f"validate_{event_prefix}")
@@ -878,6 +889,8 @@ class BulkOperationCoordinator:
878
889
  from django_bulk_hooks.helpers import build_changeset_for_create
879
890
 
880
891
  create_changeset = build_changeset_for_create(self.model_cls, created_objects)
892
+ # Mark that relationships have been preloaded to avoid per-hook duplication
893
+ create_changeset.operation_meta['relationships_preloaded'] = True
881
894
 
882
895
  self._dispatch_hooks_for_models(models_in_chain, create_changeset, "after_create", bypass_hooks=False)
883
896
 
@@ -895,12 +908,48 @@ class BulkOperationCoordinator:
895
908
  update_kwargs={}, # Empty since we don't know specific fields
896
909
  old_records_map=old_records_map,
897
910
  )
911
+ # Mark that relationships have been preloaded to avoid per-hook duplication
912
+ update_changeset.operation_meta['relationships_preloaded'] = True
898
913
 
899
914
  self._dispatch_hooks_for_models(models_in_chain, update_changeset, "after_update", bypass_hooks=False)
900
915
 
901
916
  # Clean up temporary metadata
902
917
  self._cleanup_upsert_metadata(result_objects)
903
918
 
919
+ def _extract_condition_relationships_for_operation(self, changeset, models_in_chain):
920
+ """
921
+ Extract relationships needed by hook conditions for this specific operation.
922
+
923
+ This is different from _extract_hook_relationships which gets ALL possible relationships
924
+ for queryset operations. This method only gets relationships needed by hooks that will
925
+ actually run in this operation.
926
+
927
+ Args:
928
+ changeset: The changeset for this operation
929
+ models_in_chain: List of model classes in inheritance chain
930
+
931
+ Returns:
932
+ set: Set of relationship field names to preload
933
+ """
934
+ relationships = set()
935
+ dispatcher = self.dispatcher
936
+
937
+ # Get the events that will run in this operation
938
+ event_prefix = changeset.operation_type
939
+ events_to_check = [f"validate_{event_prefix}", f"before_{event_prefix}", f"after_{event_prefix}"]
940
+
941
+ for model_cls in models_in_chain:
942
+ for event in events_to_check:
943
+ hooks = dispatcher.registry.get_hooks(model_cls, event)
944
+
945
+ for handler_cls, method_name, condition, priority in hooks:
946
+ # Only extract relationships from conditions (not @select_related)
947
+ if condition:
948
+ condition_relationships = dispatcher._extract_condition_relationships(condition, model_cls)
949
+ relationships.update(condition_relationships)
950
+
951
+ return relationships
952
+
904
953
  def _extract_hook_relationships(self):
905
954
  """
906
955
  Extract all relationship paths that hooks might access for this model and its MTI parents.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.2.72
3
+ Version: 0.2.73
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
@@ -4,7 +4,7 @@ django_bulk_hooks/conditions.py,sha256=ar4pGjtxLKmgSIlO4S6aZFKmaBNchLtxMmWpkn4g9
4
4
  django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
5
5
  django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
6
6
  django_bulk_hooks/decorators.py,sha256=TdkO4FJyFrVU2zqK6Y_6JjEJ4v3nbKkk7aa22jN10sk,11994
7
- django_bulk_hooks/dispatcher.py,sha256=AJJFpP5zvstF1VfKyduetDlor9Dskk-7VbwHkGHRMYM,22352
7
+ django_bulk_hooks/dispatcher.py,sha256=gQ8LYATpgYInKk-zzumV8g0Uq3tLh9arRUBR2rYvOp8,22724
8
8
  django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
9
9
  django_bulk_hooks/factory.py,sha256=ezrVM5U023KZqOBbJXb6lYUP-pE7WJmi8Olh2Ew-7RA,18085
10
10
  django_bulk_hooks/handler.py,sha256=SRCrMzgolrruTkvMnYBFmXLR-ABiw0JiH3605PEdCZM,4207
@@ -14,14 +14,14 @@ django_bulk_hooks/models.py,sha256=TWN_F-SsLGPx9jrkNT9pmJFR5VsZ0Z_QaVOZOmt7bpw,2
14
14
  django_bulk_hooks/operations/__init__.py,sha256=BtJYjmRhe_sScivLsniDaZmBkm0ZLvcmzXFKL7QY2Xg,550
15
15
  django_bulk_hooks/operations/analyzer.py,sha256=Pz8mc-EL8KDOfLQFYiRuN-r0OmINW3nIBhRJJCma-yo,10360
16
16
  django_bulk_hooks/operations/bulk_executor.py,sha256=po8V_2H3ULiE0RYJ-wbaRIz52SKhss81UHwuQjlz3H8,26214
17
- django_bulk_hooks/operations/coordinator.py,sha256=Gq2vqjlIyyfV8OSoipoED2hC2kb2czaHuy2Cs3gtmGI,39751
17
+ django_bulk_hooks/operations/coordinator.py,sha256=J3lsxsN7lgBkeiSmu1Kw0tvU_M9sBTozdLZ7HO7Nb-w,42489
18
18
  django_bulk_hooks/operations/field_utils.py,sha256=cQ9w4xdk-z3PrMLFvRzVV07Wc0D2qbpSepwoupqwQH8,7888
19
19
  django_bulk_hooks/operations/mti_handler.py,sha256=x1uNvP8MkidifPp_AMp4nffsdW3dz3izV6SeaLJ0DaA,26247
20
20
  django_bulk_hooks/operations/mti_plans.py,sha256=HIRJgogHPpm6MV7nZZ-sZhMLUnozpZPV2SzwQHLRzYc,3667
21
21
  django_bulk_hooks/operations/record_classifier.py,sha256=It85hJC2K-UsEOLbTR-QBdY5UPV-acQIJ91TSGa7pYo,7053
22
22
  django_bulk_hooks/queryset.py,sha256=tHt1U2O9Hvozg9sdn5MjzAk_I6wDU-LupuKFTfv1SiQ,7449
23
23
  django_bulk_hooks/registry.py,sha256=4HxP1mVK2z4VzvlohbEw2359wM21UJZJYagJJ1komM0,7947
24
- django_bulk_hooks-0.2.72.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
25
- django_bulk_hooks-0.2.72.dist-info/METADATA,sha256=QkD3zT88vaJBu79C5vYBSORg0LBh4nCzVpwObeLwpko,10555
26
- django_bulk_hooks-0.2.72.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
- django_bulk_hooks-0.2.72.dist-info/RECORD,,
24
+ django_bulk_hooks-0.2.73.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
25
+ django_bulk_hooks-0.2.73.dist-info/METADATA,sha256=6bVemHt8jIKA8HKockPGTjjnh1KZVWMb3MJnzwoLdto,10555
26
+ django_bulk_hooks-0.2.73.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
+ django_bulk_hooks-0.2.73.dist-info/RECORD,,