django-bulk-hooks 0.1.245__py3-none-any.whl → 0.1.247__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.

@@ -172,21 +172,18 @@ class HookQuerySetMixin:
172
172
  logger.debug("update: running hooks with Salesforce-style behavior")
173
173
  ctx = HookContext(model_cls, bypass_hooks=False)
174
174
 
175
- # For Subquery updates, we need to run hooks AFTER the database update and refresh
176
- # For non-Subquery updates, we can run hooks before the database update as usual
175
+ # Run validation hooks first
176
+ engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
177
+
178
+ # For Subquery updates, skip BEFORE_UPDATE hooks here - they'll run after refresh
177
179
  if not has_subquery:
178
- # Run validation hooks first
179
- engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
180
- # Then run BEFORE_UPDATE hooks
180
+ # Then run BEFORE_UPDATE hooks for non-Subquery updates
181
181
  engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
182
182
 
183
183
  # Persist any additional field mutations made by BEFORE_UPDATE hooks.
184
184
  # Build CASE statements per modified field not already present in kwargs.
185
- modified_fields = (
186
- self._detect_modified_fields(instances, originals)
187
- if not has_subquery
188
- else set()
189
- )
185
+ # Note: For Subquery updates, this will be empty since hooks haven't run yet
186
+ modified_fields = self._detect_modified_fields(instances, originals)
190
187
  extra_fields = [f for f in modified_fields if f not in kwargs]
191
188
  if extra_fields:
192
189
  case_statements = {}
@@ -384,7 +381,7 @@ class HookQuerySetMixin:
384
381
  raise
385
382
 
386
383
  # If we used Subquery objects, refresh the instances to get computed values
387
- # and then run hooks so HasChanged conditions work correctly
384
+ # and run BEFORE_UPDATE hooks so HasChanged conditions work correctly
388
385
  if has_subquery and instances and not current_bypass_hooks:
389
386
  logger.debug(
390
387
  "Refreshing instances with Subquery computed values before running hooks"
@@ -395,26 +392,47 @@ class HookQuerySetMixin:
395
392
  obj.pk: obj for obj in model_cls._base_manager.filter(pk__in=pks)
396
393
  }
397
394
 
398
- # Bulk update all instances in memory
395
+ # Bulk update all instances in memory and save pre-hook state
396
+ pre_hook_state = {}
399
397
  for instance in instances:
400
398
  if instance.pk in refreshed_instances:
401
399
  refreshed_instance = refreshed_instances[instance.pk]
402
- # Update all fields except primary key
400
+ # Save current state before modifying for hook comparison
401
+ pre_hook_values = {}
403
402
  for field in model_cls._meta.fields:
404
403
  if field.name != "id":
404
+ pre_hook_values[field.name] = getattr(refreshed_instance, field.name)
405
405
  setattr(
406
406
  instance,
407
407
  field.name,
408
408
  getattr(refreshed_instance, field.name),
409
409
  )
410
+ pre_hook_state[instance.pk] = pre_hook_values
410
411
 
411
- # Now run the hooks with the refreshed instances containing computed values
412
- logger.debug("Running hooks after Subquery refresh")
413
- # Run validation hooks first
414
- engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
415
- # Then run BEFORE_UPDATE hooks
412
+ # Now run BEFORE_UPDATE hooks with refreshed instances so conditions work
413
+ logger.debug("Running BEFORE_UPDATE hooks after Subquery refresh")
416
414
  engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
417
415
 
416
+ # Check if hooks modified any fields and persist them with bulk_update
417
+ hook_modified_fields = set()
418
+ for instance in instances:
419
+ if instance.pk in pre_hook_state:
420
+ pre_hook_values = pre_hook_state[instance.pk]
421
+ for field_name, pre_hook_value in pre_hook_values.items():
422
+ current_value = getattr(instance, field_name)
423
+ if current_value != pre_hook_value:
424
+ hook_modified_fields.add(field_name)
425
+
426
+ hook_modified_fields = list(hook_modified_fields)
427
+ if hook_modified_fields:
428
+ logger.debug(
429
+ f"Running bulk_update for hook-modified fields: {hook_modified_fields}"
430
+ )
431
+ # Use bulk_update to persist hook modifications, bypassing hooks to avoid recursion
432
+ model_cls.objects.bulk_update(
433
+ instances, hook_modified_fields, bypass_hooks=True
434
+ )
435
+
418
436
  # Salesforce-style: Always run AFTER_UPDATE hooks unless explicitly bypassed
419
437
  if not current_bypass_hooks:
420
438
  logger.debug("update: running AFTER_UPDATE")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.245
3
+ Version: 0.1.247
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
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=e_GACTQT-pFF-zL7POeo232MgOikUoCLcxDVInAUiBw,
9
9
  django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
10
  django_bulk_hooks/models.py,sha256=WtSfc4GBOG_oOt8n37cVvid0MtFIGze9JYKSixil2y0,4370
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=4fhRS44IZfhCyUdqqcTp7p9XUyGTLcNoViFNSPEP88I,50995
12
+ django_bulk_hooks/queryset.py,sha256=CHcQzKNQvdYWnIUHqG5tkngWtiBKEvyEeJMXUpNkTzc,52108
13
13
  django_bulk_hooks/registry.py,sha256=GRUTGVQEO2sdkC9OaZ9Q3U7mM-3Ix83uTyvrlTtpatw,1317
14
- django_bulk_hooks-0.1.245.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.245.dist-info/METADATA,sha256=HiXQqF3rkZYjfHL79nFgGmPOo8GKB2i6Fd39Jm_FYFI,9049
16
- django_bulk_hooks-0.1.245.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
- django_bulk_hooks-0.1.245.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.247.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.247.dist-info/METADATA,sha256=yyIsZ_9DgoIzDl7y6fCvPTW2W-Asc7Dtfuo0Tlj5fcA,9049
16
+ django_bulk_hooks-0.1.247.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
+ django_bulk_hooks-0.1.247.dist-info/RECORD,,