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

@@ -1,4 +1,5 @@
1
1
  import logging
2
+
2
3
  from django.db import models, transaction
3
4
  from django.db.models import AutoField, Case, Field, Value, When
4
5
 
@@ -70,44 +71,26 @@ class HookQuerySetMixin:
70
71
  for value in kwargs.values()
71
72
  )
72
73
 
73
- # Apply field updates to instances
74
- for obj in instances:
75
- for field, value in kwargs.items():
76
- setattr(obj, field, value)
77
-
78
74
  # Check if we're in a bulk operation context to prevent double hook execution
79
75
  from django_bulk_hooks.context import get_bypass_hooks
76
+
80
77
  current_bypass_hooks = get_bypass_hooks()
81
-
82
- # If we're in a bulk operation context, skip hooks to prevent double execution
83
- if current_bypass_hooks:
84
- logger.debug("update: skipping hooks (bulk context)")
85
- ctx = HookContext(model_cls, bypass_hooks=True)
86
- else:
87
- logger.debug("update: running hooks (standalone)")
88
- ctx = HookContext(model_cls, bypass_hooks=False)
89
- # Run validation hooks first
90
- engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
91
- # Then run BEFORE_UPDATE hooks
92
- engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
93
78
 
94
- # Use Django's built-in update logic directly
95
- # Call the base QuerySet implementation to avoid recursion
96
- update_count = super().update(**kwargs)
79
+ # If we used Subquery objects, we need to resolve them before hooks to avoid comparison errors
80
+ if has_subquery and not current_bypass_hooks:
81
+ # Execute the Django update first to compute subquery values
82
+ update_count = super().update(**kwargs)
97
83
 
98
- # If we used Subquery objects, refresh the instances to get computed values
99
- if has_subquery and instances:
100
- # Simple refresh of model fields without fetching related objects
101
- # Subquery updates only affect the model's own fields, not relationships
84
+ # Refresh instances to get computed subquery values BEFORE running hooks
102
85
  refreshed_instances = {
103
86
  obj.pk: obj for obj in model_cls._base_manager.filter(pk__in=pks)
104
87
  }
105
88
 
106
- # Bulk update all instances in memory
89
+ # Update instances in memory with computed values
107
90
  for instance in instances:
108
91
  if instance.pk in refreshed_instances:
109
92
  refreshed_instance = refreshed_instances[instance.pk]
110
- # Update all fields except primary key
93
+ # Update all fields except primary key with the computed values
111
94
  for field in model_cls._meta.fields:
112
95
  if field.name != "id":
113
96
  setattr(
@@ -116,12 +99,36 @@ class HookQuerySetMixin:
116
99
  getattr(refreshed_instance, field.name),
117
100
  )
118
101
 
102
+ # Now run hooks with resolved subquery values
103
+ ctx = HookContext(model_cls, bypass_hooks=False)
104
+ # Run validation hooks first
105
+ engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
106
+ # Then run BEFORE_UPDATE hooks
107
+ engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
108
+ else:
109
+ # Apply field updates to instances for non-subquery cases
110
+ for obj in instances:
111
+ for field, value in kwargs.items():
112
+ setattr(obj, field, value)
113
+
114
+ # If we're in a bulk operation context, skip hooks to prevent double execution
115
+ if current_bypass_hooks:
116
+ ctx = HookContext(model_cls, bypass_hooks=True)
117
+ # For bulk operations without hooks, execute Django update if not done already
118
+ update_count = super().update(**kwargs)
119
+ else:
120
+ ctx = HookContext(model_cls, bypass_hooks=False)
121
+ # Run validation hooks first
122
+ engine.run(model_cls, VALIDATE_UPDATE, instances, originals, ctx=ctx)
123
+ # Then run BEFORE_UPDATE hooks
124
+ engine.run(model_cls, BEFORE_UPDATE, instances, originals, ctx=ctx)
125
+
126
+ # Execute Django update if not done already (for non-subquery cases)
127
+ update_count = super().update(**kwargs)
128
+
119
129
  # Run AFTER_UPDATE hooks only for standalone updates
120
130
  if not current_bypass_hooks:
121
- logger.debug("update: running AFTER_UPDATE")
122
131
  engine.run(model_cls, AFTER_UPDATE, instances, originals, ctx=ctx)
123
- else:
124
- logger.debug("update: skipping AFTER_UPDATE (bulk context)")
125
132
 
126
133
  return update_count
127
134
 
@@ -180,12 +187,12 @@ class HookQuerySetMixin:
180
187
 
181
188
  # Fire hooks before DB ops
182
189
  if not bypass_hooks:
183
- ctx = HookContext(model_cls, bypass_hooks=False) # Pass bypass_hooks
190
+ ctx = HookContext(model_cls, bypass_hooks=False) # Pass bypass_hooks
184
191
  if not bypass_validation:
185
192
  engine.run(model_cls, VALIDATE_CREATE, objs, ctx=ctx)
186
193
  engine.run(model_cls, BEFORE_CREATE, objs, ctx=ctx)
187
194
  else:
188
- ctx = HookContext(model_cls, bypass_hooks=True) # Pass bypass_hooks
195
+ ctx = HookContext(model_cls, bypass_hooks=True) # Pass bypass_hooks
189
196
  logger.debug("bulk_create bypassed hooks")
190
197
 
191
198
  # For MTI models, we need to handle them specially
@@ -241,7 +248,9 @@ class HookQuerySetMixin:
241
248
  f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
242
249
  )
243
250
 
244
- logger.debug(f"bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}")
251
+ logger.debug(
252
+ f"bulk_update {model_cls.__name__} bypass_hooks={bypass_hooks} objs={len(objs)}"
253
+ )
245
254
 
246
255
  # Check for MTI
247
256
  is_mti = False
@@ -257,7 +266,9 @@ class HookQuerySetMixin:
257
266
  else:
258
267
  logger.debug("bulk_update: hooks bypassed")
259
268
  ctx = HookContext(model_cls, bypass_hooks=True)
260
- originals = [None] * len(objs) # Ensure originals is defined for after_update call
269
+ originals = [None] * len(
270
+ objs
271
+ ) # Ensure originals is defined for after_update call
261
272
 
262
273
  # Handle auto_now fields like Django's update_or_create does
263
274
  fields_set = set(fields)
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.1
2
2
  Name: django-bulk-hooks
3
- Version: 0.1.221
3
+ Version: 0.1.222
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
+ Home-page: https://github.com/AugendLimited/django-bulk-hooks
5
6
  License: MIT
6
7
  Keywords: django,bulk,hooks
7
8
  Author: Konrad Beck
@@ -13,7 +14,6 @@ Classifier: Programming Language :: Python :: 3.11
13
14
  Classifier: Programming Language :: Python :: 3.12
14
15
  Classifier: Programming Language :: Python :: 3.13
15
16
  Requires-Dist: Django (>=4.0)
16
- Project-URL: Homepage, https://github.com/AugendLimited/django-bulk-hooks
17
17
  Project-URL: Repository, https://github.com/AugendLimited/django-bulk-hooks
18
18
  Description-Content-Type: text/markdown
19
19
 
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,
9
9
  django_bulk_hooks/manager.py,sha256=nfWiwU5-yAoxdnQsUMohxtyCpkV0MBv6X3wmipr9eQY,3697
10
10
  django_bulk_hooks/models.py,sha256=exnXYVKEVbYAXhChCP8VdWTnKCnm9DiTcokEIBee1I0,4350
11
11
  django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
12
- django_bulk_hooks/queryset.py,sha256=YSDCMAf24YLeLJrRKCDYwbZInP2mK_Tcuw3EHLDkv_w,32605
12
+ django_bulk_hooks/queryset.py,sha256=INcmUxugAxDY8M2Swvv4lItyniejT1CNUpJtEnFfoBw,33087
13
13
  django_bulk_hooks/registry.py,sha256=8UuhniiH5ChSeOKV1UUbqTEiIu25bZXvcHmkaRbxmME,1131
14
- django_bulk_hooks-0.1.221.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
- django_bulk_hooks-0.1.221.dist-info/METADATA,sha256=QVFXKdg3v7WcK1IsfDD3h3Nijg3tXdHtlqS6JoGemFo,9061
16
- django_bulk_hooks-0.1.221.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
17
- django_bulk_hooks-0.1.221.dist-info/RECORD,,
14
+ django_bulk_hooks-0.1.222.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
15
+ django_bulk_hooks-0.1.222.dist-info/METADATA,sha256=QJazHusCLVY-qjWHDauyE9KUPPsnvGOLTd6ral0T9Wc,9049
16
+ django_bulk_hooks-0.1.222.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
17
+ django_bulk_hooks-0.1.222.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.1.3
2
+ Generator: poetry-core 1.9.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any