django-bulk-hooks 0.1.179__py3-none-any.whl → 0.1.180__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.
- django_bulk_hooks/queryset.py +79 -29
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.180.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.180.dist-info}/RECORD +5 -5
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.180.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.1.179.dist-info → django_bulk_hooks-0.1.180.dist-info}/WHEEL +0 -0
django_bulk_hooks/queryset.py
CHANGED
|
@@ -24,22 +24,22 @@ class HookQuerySet(models.QuerySet):
|
|
|
24
24
|
objs = list(self)
|
|
25
25
|
if not objs:
|
|
26
26
|
return 0
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
model_cls = self.model
|
|
29
29
|
ctx = HookContext(model_cls)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Run validation hooks first
|
|
32
32
|
engine.run(model_cls, VALIDATE_DELETE, objs, ctx=ctx)
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
# Then run business logic hooks
|
|
35
35
|
engine.run(model_cls, BEFORE_DELETE, objs, ctx=ctx)
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
# Use Django's standard delete() method
|
|
38
38
|
result = super().delete()
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
# Run AFTER_DELETE hooks
|
|
41
41
|
engine.run(model_cls, AFTER_DELETE, objs, ctx=ctx)
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
return result
|
|
44
44
|
|
|
45
45
|
@transaction.atomic
|
|
@@ -109,10 +109,10 @@ class HookQuerySet(models.QuerySet):
|
|
|
109
109
|
# trickier so it's not done yet.
|
|
110
110
|
if batch_size is not None and batch_size <= 0:
|
|
111
111
|
raise ValueError("Batch size must be a positive integer.")
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
if not objs:
|
|
114
114
|
return objs
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
if any(not isinstance(obj, model_cls) for obj in objs):
|
|
117
117
|
raise TypeError(
|
|
118
118
|
f"bulk_create expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
@@ -177,7 +177,7 @@ class HookQuerySet(models.QuerySet):
|
|
|
177
177
|
self, objs, fields, bypass_hooks=False, bypass_validation=False, **kwargs
|
|
178
178
|
):
|
|
179
179
|
"""
|
|
180
|
-
Bulk update objects in the database.
|
|
180
|
+
Bulk update objects in the database with MTI support.
|
|
181
181
|
"""
|
|
182
182
|
model_cls = self.model
|
|
183
183
|
|
|
@@ -189,9 +189,15 @@ class HookQuerySet(models.QuerySet):
|
|
|
189
189
|
f"bulk_update expected instances of {model_cls.__name__}, but got {set(type(obj).__name__ for obj in objs)}"
|
|
190
190
|
)
|
|
191
191
|
|
|
192
|
+
# Check for MTI
|
|
193
|
+
is_mti = False
|
|
194
|
+
for parent in model_cls._meta.all_parents:
|
|
195
|
+
if parent._meta.concrete_model is not model_cls._meta.concrete_model:
|
|
196
|
+
is_mti = True
|
|
197
|
+
break
|
|
198
|
+
|
|
192
199
|
if not bypass_hooks:
|
|
193
|
-
# Load originals for hook comparison
|
|
194
|
-
# Use the base manager to avoid recursion
|
|
200
|
+
# Load originals for hook comparison
|
|
195
201
|
original_map = {
|
|
196
202
|
obj.pk: obj
|
|
197
203
|
for obj in model_cls._base_manager.filter(
|
|
@@ -207,28 +213,31 @@ class HookQuerySet(models.QuerySet):
|
|
|
207
213
|
engine.run(model_cls, VALIDATE_UPDATE, objs, originals, ctx=ctx)
|
|
208
214
|
|
|
209
215
|
# Then run business logic hooks
|
|
210
|
-
|
|
211
|
-
engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
|
|
216
|
+
engine.run(model_cls, BEFORE_UPDATE, objs, originals, ctx=ctx)
|
|
212
217
|
|
|
213
|
-
#
|
|
218
|
+
# Detect modified fields during hooks
|
|
214
219
|
modified_fields = self._detect_modified_fields(objs, originals)
|
|
215
220
|
if modified_fields:
|
|
216
|
-
# Convert to set for efficient union operation
|
|
217
221
|
fields_set = set(fields)
|
|
218
222
|
fields_set.update(modified_fields)
|
|
219
223
|
fields = list(fields_set)
|
|
220
224
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
# Handle MTI models differently
|
|
226
|
+
if is_mti:
|
|
227
|
+
result = self._mti_bulk_update(objs, fields, **kwargs)
|
|
228
|
+
else:
|
|
229
|
+
# For single-table models, use Django's built-in bulk_update
|
|
230
|
+
django_kwargs = {
|
|
231
|
+
k: v
|
|
232
|
+
for k, v in kwargs.items()
|
|
233
|
+
if k not in ["bypass_hooks", "bypass_validation"]
|
|
234
|
+
}
|
|
235
|
+
result = super().bulk_update(objs, fields, **django_kwargs)
|
|
227
236
|
|
|
228
237
|
if not bypass_hooks:
|
|
229
238
|
engine.run(model_cls, AFTER_UPDATE, objs, originals, ctx=ctx)
|
|
230
239
|
|
|
231
|
-
return
|
|
240
|
+
return result
|
|
232
241
|
|
|
233
242
|
def _detect_modified_fields(self, new_instances, original_instances):
|
|
234
243
|
"""
|
|
@@ -302,13 +311,6 @@ class HookQuerySet(models.QuerySet):
|
|
|
302
311
|
if inheritance_chain is None:
|
|
303
312
|
inheritance_chain = self._get_inheritance_chain()
|
|
304
313
|
|
|
305
|
-
print("inheritance_chain")
|
|
306
|
-
print(inheritance_chain)
|
|
307
|
-
|
|
308
|
-
for inheritance in inheritance_chain:
|
|
309
|
-
print(inheritance)
|
|
310
|
-
|
|
311
|
-
|
|
312
314
|
# Safety check to prevent infinite recursion
|
|
313
315
|
if len(inheritance_chain) > 10: # Arbitrary limit to prevent infinite loops
|
|
314
316
|
raise ValueError(
|
|
@@ -527,3 +529,51 @@ class HookQuerySet(models.QuerySet):
|
|
|
527
529
|
field.pre_save(child_obj, add=True)
|
|
528
530
|
|
|
529
531
|
return child_obj
|
|
532
|
+
|
|
533
|
+
def _mti_bulk_update(self, objs, fields, **kwargs):
|
|
534
|
+
"""
|
|
535
|
+
Custom bulk update implementation for MTI models.
|
|
536
|
+
Updates each table in the inheritance chain efficiently using Django's batch_size.
|
|
537
|
+
"""
|
|
538
|
+
model_cls = self.model
|
|
539
|
+
inheritance_chain = self._get_inheritance_chain()
|
|
540
|
+
|
|
541
|
+
# Group fields by model in the inheritance chain
|
|
542
|
+
field_groups = {}
|
|
543
|
+
for field_name in fields:
|
|
544
|
+
field = model_cls._meta.get_field(field_name)
|
|
545
|
+
# Find which model in the inheritance chain this field belongs to
|
|
546
|
+
for model in inheritance_chain:
|
|
547
|
+
if field in model._meta.local_fields:
|
|
548
|
+
if model not in field_groups:
|
|
549
|
+
field_groups[model] = []
|
|
550
|
+
field_groups[model].append(field_name)
|
|
551
|
+
break
|
|
552
|
+
|
|
553
|
+
# Update each table in the inheritance chain
|
|
554
|
+
total_updated = 0
|
|
555
|
+
for model, model_fields in field_groups.items():
|
|
556
|
+
if not model_fields:
|
|
557
|
+
continue
|
|
558
|
+
|
|
559
|
+
# Get objects that have this model's fields
|
|
560
|
+
model_objs = []
|
|
561
|
+
for obj in objs:
|
|
562
|
+
# Create a temporary object with just this model's fields
|
|
563
|
+
temp_obj = model()
|
|
564
|
+
temp_obj.pk = obj.pk # Set the primary key
|
|
565
|
+
|
|
566
|
+
# Copy only the fields for this model
|
|
567
|
+
for field_name in model_fields:
|
|
568
|
+
if hasattr(obj, field_name):
|
|
569
|
+
setattr(temp_obj, field_name, getattr(obj, field_name))
|
|
570
|
+
|
|
571
|
+
model_objs.append(temp_obj)
|
|
572
|
+
|
|
573
|
+
# Use Django's bulk_update for this model's table
|
|
574
|
+
# This will automatically use batch_size from kwargs
|
|
575
|
+
base_qs = model._base_manager.using(self.db)
|
|
576
|
+
updated_count = base_qs.bulk_update(model_objs, model_fields, **kwargs)
|
|
577
|
+
total_updated += updated_count
|
|
578
|
+
|
|
579
|
+
return total_updated
|
|
@@ -9,9 +9,9 @@ django_bulk_hooks/handler.py,sha256=xZt8iNdYF-ACz-MnKMY0co6scWINU5V5wC1lyDn844k,
|
|
|
9
9
|
django_bulk_hooks/manager.py,sha256=KLEjpQRt4WlzgBAf_X3XOAPUQM8Jmc1fIt8yr62FPQc,3044
|
|
10
10
|
django_bulk_hooks/models.py,sha256=7fnx5xd4HWXfLVlFhhiRzR92JRWFEuxgk6aSWLEsyJg,3996
|
|
11
11
|
django_bulk_hooks/priority.py,sha256=HG_2D35nga68lBCZmSXTcplXrjFoRgZFRDOy4ROKonY,376
|
|
12
|
-
django_bulk_hooks/queryset.py,sha256
|
|
12
|
+
django_bulk_hooks/queryset.py,sha256=ezIjOPUJ-1Oya_jwnJlPBg0kNkefbdyKmHBPFXYLBkY,24467
|
|
13
13
|
django_bulk_hooks/registry.py,sha256=-mQBizJ06nz_tajZBinViKx_uP2Tbc1tIpTEMv7lwKA,705
|
|
14
|
-
django_bulk_hooks-0.1.
|
|
15
|
-
django_bulk_hooks-0.1.
|
|
16
|
-
django_bulk_hooks-0.1.
|
|
17
|
-
django_bulk_hooks-0.1.
|
|
14
|
+
django_bulk_hooks-0.1.180.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
15
|
+
django_bulk_hooks-0.1.180.dist-info/METADATA,sha256=4zH19E4nseZM2tF5kdJoTpvcJOdEtPJ42PGjZXPY0Eg,6951
|
|
16
|
+
django_bulk_hooks-0.1.180.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
17
|
+
django_bulk_hooks-0.1.180.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|